git rid of enums/by_table

This commit is contained in:
Brandon Thomas committed 2026-03-21 18:59:50 -04:00
1 parent c9be9bf744
commit cff0624ffc
89 files changed
+8 -7736

No files matched your search

Generated
+1
View File
@@ -3794,6 +3794,7 @@ dependencies = [
name = "enums"
version = "0.0.1"
dependencies = [
"enums_db",
"log",
"serde",
"serde_derive",
+1 -1
View File
@@ -24,7 +24,7 @@ database = ["sqlx", "sqlx-core", "sqlx-mysql"]
workspace-build-acceleration.workspace = true
# Internal
# (None yet)
enums_db.workspace = true
# Serialization
serde.workspace = true
@@ -1,4 +1,4 @@
use crate::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums_db::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use utoipa::ToSchema;
/// A forward-compatible version of `FrontendFailureCategory` for API clients.
@@ -1,4 +1,4 @@
use crate::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums_db::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use utoipa::ToSchema;
/// A restricted subset of `FrontendFailureCategory` that only includes values
@@ -1,110 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `audit_logs` table in a `VARCHAR(32)` field named `entity_action`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum AuditLogEntityAction {
/// Create action
#[serde(rename = "create")]
Create,
/// Edit action
#[serde(rename = "edit")]
Edit,
/// Edit features (eg. user feature flags)
#[serde(rename = "edit_features")]
EditFeatures,
/// Delete action
#[serde(rename = "delete")]
Delete,
/// Create featured item
#[serde(rename = "featured_item_create")]
FeaturedItemCreate,
/// Delete featured item
#[serde(rename = "featured_item_delete")]
FeaturedItemDelete,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(AuditLogEntityAction);
impl_mysql_enum_coders!(AuditLogEntityAction);
/// NB: Legacy API for older code.
impl AuditLogEntityAction {
pub fn to_str(&self) -> &'static str {
match self {
Self::Create => "create",
Self::Edit => "edit",
Self::EditFeatures => "edit_features",
Self::Delete => "delete",
Self::FeaturedItemCreate => "featured_item_create",
Self::FeaturedItemDelete => "featured_item_delete",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"create" => Ok(Self::Create),
"edit" => Ok(Self::Edit),
"edit_features" => Ok(Self::EditFeatures),
"delete" => Ok(Self::Delete),
"featured_item_create" => Ok(Self::FeaturedItemCreate),
"featured_item_delete" => Ok(Self::FeaturedItemDelete),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::audit_logs::audit_log_entity_action::AuditLogEntityAction;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(AuditLogEntityAction::Create, "create");
assert_serialization(AuditLogEntityAction::Edit, "edit");
assert_serialization(AuditLogEntityAction::EditFeatures, "edit_features");
assert_serialization(AuditLogEntityAction::Delete, "delete");
assert_serialization(AuditLogEntityAction::FeaturedItemCreate, "featured_item_create");
assert_serialization(AuditLogEntityAction::FeaturedItemDelete, "featured_item_delete");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(AuditLogEntityAction::Create.to_str(), "create");
assert_eq!(AuditLogEntityAction::Edit.to_str(), "edit");
assert_eq!(AuditLogEntityAction::EditFeatures.to_str(), "edit_features");
assert_eq!(AuditLogEntityAction::Delete.to_str(), "delete");
assert_eq!(AuditLogEntityAction::FeaturedItemCreate.to_str(), "featured_item_create");
assert_eq!(AuditLogEntityAction::FeaturedItemDelete.to_str(), "featured_item_delete");
}
#[test]
fn test_from_str() {
assert_eq!(AuditLogEntityAction::from_str("create").unwrap(), AuditLogEntityAction::Create);
assert_eq!(AuditLogEntityAction::from_str("edit").unwrap(), AuditLogEntityAction::Edit);
assert_eq!(AuditLogEntityAction::from_str("edit_features").unwrap(), AuditLogEntityAction::EditFeatures);
assert_eq!(AuditLogEntityAction::from_str("delete").unwrap(), AuditLogEntityAction::Delete);
assert_eq!(AuditLogEntityAction::from_str("featured_item_create").unwrap(), AuditLogEntityAction::FeaturedItemCreate);
assert_eq!(AuditLogEntityAction::from_str("featured_item_delete").unwrap(), AuditLogEntityAction::FeaturedItemDelete);
assert!(AuditLogEntityAction::from_str("foo").is_err());
}
}
}
@@ -1,92 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `audit_logs` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum AuditLogEntityType {
/// Comment system
#[serde(rename = "comment")]
Comment,
/// Media file
#[serde(rename = "media_file")]
MediaFile,
/// Model weight
#[serde(rename = "model_weight")]
ModelWeight,
/// User
#[serde(rename = "user")]
User,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(AuditLogEntityType);
impl_mysql_enum_coders!(AuditLogEntityType);
/// NB: Legacy API for older code.
impl AuditLogEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Comment => "comment",
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
Self::User => "user",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"comment" => Ok(Self::Comment),
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
"user" => Ok(Self::User),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::audit_logs::audit_log_entity_type::AuditLogEntityType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(AuditLogEntityType::Comment, "comment");
assert_serialization(AuditLogEntityType::MediaFile, "media_file");
assert_serialization(AuditLogEntityType::ModelWeight, "model_weight");
assert_serialization(AuditLogEntityType::User, "user");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(AuditLogEntityType::Comment.to_str(), "comment");
assert_eq!(AuditLogEntityType::MediaFile.to_str(), "media_file");
assert_eq!(AuditLogEntityType::ModelWeight.to_str(), "model_weight");
assert_eq!(AuditLogEntityType::User.to_str(), "user");
}
#[test]
fn test_from_str() {
assert_eq!(AuditLogEntityType::from_str("comment").unwrap(), AuditLogEntityType::Comment);
assert_eq!(AuditLogEntityType::from_str("media_file").unwrap(), AuditLogEntityType::MediaFile);
assert_eq!(AuditLogEntityType::from_str("model_weight").unwrap(), AuditLogEntityType::ModelWeight);
assert_eq!(AuditLogEntityType::from_str("user").unwrap(), AuditLogEntityType::User);
assert!(AuditLogEntityType::from_str("foo").is_err());
}
}
}
@@ -1,2 +0,0 @@
pub mod audit_log_entity_action;
pub mod audit_log_entity_type;
@@ -1,109 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `batch_generations` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, Ord, PartialOrd, ToSchema)]
pub enum BatchGenerationEntityType {
/// Media files
/// This will probably be the only type supported, but we'll leave the possibility of future types.
#[serde(rename = "media_file")]
MediaFile,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(BatchGenerationEntityType);
impl_mysql_enum_coders!(BatchGenerationEntityType);
impl_mysql_from_row!(BatchGenerationEntityType);
/// NB: Legacy API for older code.
impl BatchGenerationEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::MediaFile,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::batch_generations::batch_generation_entity_type::BatchGenerationEntityType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(BatchGenerationEntityType::MediaFile, "media_file");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(BatchGenerationEntityType::MediaFile.to_str(), "media_file");
}
#[test]
fn from_str() {
assert_eq!(BatchGenerationEntityType::from_str("media_file").unwrap(), BatchGenerationEntityType::MediaFile);
assert!(BatchGenerationEntityType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = BatchGenerationEntityType::all_variants();
assert_eq!(variants.len(), 1);
assert_eq!(variants.pop_first(), Some(BatchGenerationEntityType::MediaFile));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(BatchGenerationEntityType::all_variants().len(), BatchGenerationEntityType::iter().len());
}
#[test]
fn round_trip() {
for variant in BatchGenerationEntityType::all_variants() {
assert_eq!(variant, BatchGenerationEntityType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, BatchGenerationEntityType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, BatchGenerationEntityType::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1 +0,0 @@
pub mod batch_generation_entity_type;
@@ -1,109 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `batch_generations` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, Ord, PartialOrd, ToSchema)]
pub enum BetaKeyProduct {
/// Media files
/// This will probably be the only type supported, but we'll leave the possibility of future types.
#[serde(rename = "studio")]
Studio,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(BetaKeyProduct);
impl_mysql_enum_coders!(BetaKeyProduct);
impl_mysql_from_row!(BetaKeyProduct);
/// NB: Legacy API for older code.
impl BetaKeyProduct {
pub fn to_str(&self) -> &'static str {
match self {
Self::Studio => "studio",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"studio" => Ok(Self::Studio),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Studio,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::beta_keys::beta_key_product::BetaKeyProduct;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(BetaKeyProduct::Studio, "studio");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(BetaKeyProduct::Studio.to_str(), "studio");
}
#[test]
fn from_str() {
assert_eq!(BetaKeyProduct::from_str("studio").unwrap(), BetaKeyProduct::Studio);
assert!(BetaKeyProduct::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = BetaKeyProduct::all_variants();
assert_eq!(variants.len(), 1);
assert_eq!(variants.pop_first(), Some(BetaKeyProduct::Studio));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(BetaKeyProduct::all_variants().len(), BetaKeyProduct::iter().len());
}
#[test]
fn round_trip() {
for variant in BetaKeyProduct::all_variants() {
assert_eq!(variant, BetaKeyProduct::from_str(variant.to_str()).unwrap());
assert_eq!(variant, BetaKeyProduct::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, BetaKeyProduct::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1 +0,0 @@
pub mod beta_key_product;
@@ -1,120 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `comments` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, ToSchema)]
pub enum CommentEntityType {
/// User
#[serde(rename = "user")]
User,
/// Media files
#[serde(rename = "media_file")]
MediaFile,
/// Model weights
#[serde(rename = "model_weight")]
ModelWeight,
/// TTS model (architecture does not matter)
#[serde(rename = "tts_model")]
TtsModel,
/// TTS result (architecture does not matter)
#[serde(rename = "tts_result")]
TtsResult,
/// W2L template
#[serde(rename = "w2l_template")]
W2lTemplate,
/// W2L result
#[serde(rename = "w2l_result")]
W2lResult,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(CommentEntityType);
impl_mysql_enum_coders!(CommentEntityType);
/// NB: Legacy API for older code.
impl CommentEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::User => "user",
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
Self::TtsModel => "tts_model",
Self::TtsResult => "tts_result",
Self::W2lTemplate => "w2l_template",
Self::W2lResult => "w2l_result",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"user" => Ok(Self::User),
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
"tts_model" => Ok(Self::TtsModel),
"tts_result" => Ok(Self::TtsResult),
"w2l_template" => Ok(Self::W2lTemplate),
"w2l_result" => Ok(Self::W2lResult),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::comments::comment_entity_type::CommentEntityType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(CommentEntityType::User, "user");
assert_serialization(CommentEntityType::MediaFile, "media_file");
assert_serialization(CommentEntityType::ModelWeight, "model_weight");
assert_serialization(CommentEntityType::TtsModel, "tts_model");
assert_serialization(CommentEntityType::TtsResult, "tts_result");
assert_serialization(CommentEntityType::W2lTemplate, "w2l_template");
assert_serialization(CommentEntityType::W2lResult, "w2l_result");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(CommentEntityType::User.to_str(), "user");
assert_eq!(CommentEntityType::MediaFile.to_str(), "media_file");
assert_eq!(CommentEntityType::ModelWeight.to_str(), "model_weight");
assert_eq!(CommentEntityType::TtsModel.to_str(), "tts_model");
assert_eq!(CommentEntityType::TtsResult.to_str(), "tts_result");
assert_eq!(CommentEntityType::W2lTemplate.to_str(), "w2l_template");
assert_eq!(CommentEntityType::W2lResult.to_str(), "w2l_result");
}
#[test]
fn test_from_str() {
assert_eq!(CommentEntityType::from_str("user").unwrap(), CommentEntityType::User);
assert_eq!(CommentEntityType::from_str("media_file").unwrap(), CommentEntityType::MediaFile);
assert_eq!(CommentEntityType::from_str("model_weight").unwrap(), CommentEntityType::ModelWeight);
assert_eq!(CommentEntityType::from_str("tts_model").unwrap(), CommentEntityType::TtsModel);
assert_eq!(CommentEntityType::from_str("tts_result").unwrap(), CommentEntityType::TtsResult);
assert_eq!(CommentEntityType::from_str("w2l_template").unwrap(), CommentEntityType::W2lTemplate);
assert_eq!(CommentEntityType::from_str("w2l_result").unwrap(), CommentEntityType::W2lResult);
assert!(CommentEntityType::from_str("foo").is_err());
}
}
}
@@ -1 +0,0 @@
pub mod comment_entity_type;
@@ -1,115 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `email_sender_jobs` table in `VARCHAR(32)` field `id_category`.
///
/// This denotes the type of email being sent.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum EmailCategory {
/// User is recently registered
#[serde(rename = "welcome")]
Welcome,
/// User is resetting their password
#[serde(rename = "password_reset")]
PasswordReset,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(EmailCategory);
impl_mysql_enum_coders!(EmailCategory);
/// NB: Legacy API for older code.
impl EmailCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::Welcome => "welcome",
Self::PasswordReset => "password_reset",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"welcome" => Ok(Self::Welcome),
"password_reset" => Ok(Self::PasswordReset),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Welcome,
Self::PasswordReset,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::email_sender_jobs::email_category::EmailCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(EmailCategory::Welcome, "welcome");
assert_serialization(EmailCategory::PasswordReset, "password_reset");
}
#[test]
fn to_str() {
assert_eq!(EmailCategory::Welcome.to_str(), "welcome");
assert_eq!(EmailCategory::PasswordReset.to_str(), "password_reset");
}
#[test]
fn from_str() {
assert_eq!(EmailCategory::from_str("welcome").unwrap(), EmailCategory::Welcome);
assert_eq!(EmailCategory::from_str("password_reset").unwrap(), EmailCategory::PasswordReset);
}
#[test]
fn all_variants() {
// Static check
let mut variants = EmailCategory::all_variants();
assert_eq!(variants.len(), 2);
assert_eq!(variants.pop_first(), Some(EmailCategory::Welcome));
assert_eq!(variants.pop_first(), Some(EmailCategory::PasswordReset));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(EmailCategory::all_variants().len(), EmailCategory::iter().len());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(EmailCategory::all_variants().len(), EmailCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in EmailCategory::all_variants() {
assert_eq!(variant, EmailCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, EmailCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, EmailCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1 +0,0 @@
pub mod email_category;
@@ -1 +0,0 @@
pub mod stats_entity_type;
@@ -1,122 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `user_stats` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, ToSchema, Ord, PartialOrd)]
pub enum StatsEntityType {
/// Comment
#[serde(rename = "comment")]
Comment,
/// MediaFile
#[serde(rename = "media_file")]
MediaFile,
/// ModelWeight (the new way to store models)
#[serde(rename = "model_weight")]
ModelWeight,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(StatsEntityType);
impl_mysql_enum_coders!(StatsEntityType);
impl_mysql_from_row!(StatsEntityType);
/// NB: Legacy API for older code.
impl StatsEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Comment => "comment",
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"comment" => Ok(Self::Comment),
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Comment,
Self::MediaFile,
Self::ModelWeight,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::entity_stats::stats_entity_type::StatsEntityType;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(StatsEntityType::Comment, "comment");
assert_serialization(StatsEntityType::MediaFile, "media_file");
assert_serialization(StatsEntityType::ModelWeight, "model_weight");
}
#[test]
fn test_to_str() {
assert_eq!(StatsEntityType::Comment.to_str(), "comment");
assert_eq!(StatsEntityType::MediaFile.to_str(), "media_file");
assert_eq!(StatsEntityType::ModelWeight.to_str(), "model_weight");
}
#[test]
fn test_from_str() {
assert_eq!(StatsEntityType::from_str("comment").unwrap(), StatsEntityType::Comment);
assert_eq!(StatsEntityType::from_str("media_file").unwrap(), StatsEntityType::MediaFile);
assert_eq!(StatsEntityType::from_str("model_weight").unwrap(), StatsEntityType::ModelWeight);
assert!(StatsEntityType::from_str("foo").is_err());
}
#[test]
fn all_variants() {
let mut variants = StatsEntityType::all_variants();
assert_eq!(variants.len(), 3);
assert_eq!(variants.pop_first(), Some(StatsEntityType::Comment));
assert_eq!(variants.pop_first(), Some(StatsEntityType::MediaFile));
assert_eq!(variants.pop_first(), Some(StatsEntityType::ModelWeight));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(StatsEntityType::all_variants().len(), StatsEntityType::iter().len());
}
#[test]
fn round_trip() {
for variant in StatsEntityType::all_variants() {
assert_eq!(variant, StatsEntityType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, StatsEntityType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, StatsEntityType::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1,126 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `user_bookmarks` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
pub enum FeaturedItemEntityType {
/// MediaFile
#[serde(rename = "media_file")]
MediaFile,
/// ModelWeight (the new way to store models)
#[serde(rename = "model_weight")]
ModelWeight,
/// User
#[serde(rename = "user")]
User,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(FeaturedItemEntityType);
impl_mysql_enum_coders!(FeaturedItemEntityType);
impl_mysql_from_row!(FeaturedItemEntityType);
/// NB: Legacy API for older code.
impl FeaturedItemEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
Self::User => "user",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
"user" => Ok(Self::User),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::MediaFile,
Self::ModelWeight,
Self::User,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::featured_items::featured_item_entity_type::FeaturedItemEntityType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(FeaturedItemEntityType::MediaFile, "media_file");
assert_serialization(FeaturedItemEntityType::ModelWeight, "model_weight");
assert_serialization(FeaturedItemEntityType::User, "user");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(FeaturedItemEntityType::MediaFile.to_str(), "media_file");
assert_eq!(FeaturedItemEntityType::ModelWeight.to_str(), "model_weight");
assert_eq!(FeaturedItemEntityType::User.to_str(), "user");
}
#[test]
fn test_from_str() {
assert_eq!(FeaturedItemEntityType::from_str("media_file").unwrap(), FeaturedItemEntityType::MediaFile);
assert_eq!(FeaturedItemEntityType::from_str("model_weight").unwrap(), FeaturedItemEntityType::ModelWeight);
assert_eq!(FeaturedItemEntityType::from_str("user").unwrap(), FeaturedItemEntityType::User);
assert!(FeaturedItemEntityType::from_str("foo").is_err());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(FeaturedItemEntityType::all_variants().len(), FeaturedItemEntityType::iter().len());
}
#[test]
fn round_trip() {
for variant in FeaturedItemEntityType::all_variants() {
assert_eq!(variant, FeaturedItemEntityType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, FeaturedItemEntityType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, FeaturedItemEntityType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in FeaturedItemEntityType::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);
}
}
}
}
@@ -1 +0,0 @@
pub mod featured_item_entity_type;
@@ -1,175 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
// TODO: Use macro-derived impls
/// Our "generic downloads" pipeline supports a wide variety of ML models and other media.
/// They are serialized in the database table `generic_download_jobs` as a VARCHAR(32).
///
/// Each type of download is identified by the following enum variants.
/// These types are present in the HTTP API and database columns as serialized here.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "database", derive(sqlx::Type))]
#[cfg_attr(test, derive(EnumIter, EnumCount))]
pub enum GenericDownloadType {
/// NB: Note - this is hifigan for Tacotron2.
/// Some work will be needed to unify this with other hifigan types.
#[serde(rename = "hifigan")]
#[cfg_attr(feature = "database", sqlx(rename = "hifigan"))]
HifiGan,
/// NB: Note - this is hifigan for SoftVC (our internal codename is "rocketvc").
/// Some work will need to be done to unify this with other hifigan types.
#[serde(rename = "hifigan_rocket_vc")]
#[cfg_attr(feature = "database", sqlx(rename = "hifigan_rocket_vc"))]
HifiGanRocketVc,
/// NB: Note - this is hifigan for SoVitsSvc
/// Some work will need to be done to unify this with other hifigan types.
#[serde(rename = "hifigan_so_vits_svc")]
#[cfg_attr(feature = "database", sqlx(rename = "hifigan_so_vits_svc"))]
HifiGanSoVitsSvc,
//#[serde(rename = "melgan_vocodes")]
//#[sqlx(rename = "melgan_vocodes")]
//MelGanVocodes,
/// NB: Our external-facing name for "softvc" is rocketvc.
/// I wish we could stop being stupid about this.
#[serde(rename = "rocket_vc")]
#[cfg_attr(feature = "database", sqlx(rename = "rocket_vc"))]
RocketVc,
/// RVC (v2) voice conversion models
#[serde(rename = "rvc_v2")]
#[cfg_attr(feature = "database", sqlx(rename = "rvc_v2"))]
RvcV2,
/// so-vits-svc voice conversion models
#[serde(rename = "so_vits_svc")]
#[cfg_attr(feature = "database", sqlx(rename = "so_vits_svc"))]
SoVitsSvc,
/// Tacotron TTS models.
#[serde(rename = "tacotron2")]
#[cfg_attr(feature = "database", sqlx(rename = "tacotron2"))]
Tacotron2,
/// VITS TTS models.
#[serde(rename = "vits")]
#[cfg_attr(feature = "database", sqlx(rename = "vits"))]
Vits,
}
/// NB: Legacy API for older code.
impl GenericDownloadType {
pub fn to_str(&self) -> &'static str {
match self {
Self::HifiGan => "hifigan",
Self::HifiGanRocketVc => "hifigan_rocket_vc",
Self::HifiGanSoVitsSvc => "hifigan_so_vits_svc",
Self::RocketVc => "rocket_vc",
Self::RvcV2 => "rvc_v2",
Self::SoVitsSvc => "so_vits_svc",
Self::Tacotron2 => "tacotron2",
Self::Vits => "vits",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"hifigan" => Ok(Self::HifiGan),
"hifigan_rocket_vc" => Ok(Self::HifiGanRocketVc),
"hifigan_so_vits_svc" => Ok(Self::HifiGanSoVitsSvc),
"rocket_vc" => Ok(Self::RocketVc),
"rvc_v2" => Ok(Self::RvcV2),
"so_vits_svc" => Ok(Self::SoVitsSvc),
"tacotron2" => Ok(Self::Tacotron2),
"vits" => Ok(Self::Vits),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::HifiGan,
Self::HifiGanRocketVc,
Self::HifiGanSoVitsSvc,
Self::RocketVc,
Self::RvcV2,
Self::SoVitsSvc,
Self::Tacotron2,
Self::Vits,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_download_jobs::generic_download_type::GenericDownloadType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(GenericDownloadType::HifiGan, "hifigan");
assert_serialization(GenericDownloadType::HifiGanRocketVc, "hifigan_rocket_vc");
assert_serialization(GenericDownloadType::HifiGanSoVitsSvc, "hifigan_so_vits_svc");
assert_serialization(GenericDownloadType::RocketVc, "rocket_vc");
assert_serialization(GenericDownloadType::RvcV2, "rvc_v2");
assert_serialization(GenericDownloadType::SoVitsSvc, "so_vits_svc");
assert_serialization(GenericDownloadType::Tacotron2, "tacotron2");
assert_serialization(GenericDownloadType::Vits, "vits");
}
#[test]
fn to_str() {
assert_eq!(GenericDownloadType::HifiGan.to_str(), "hifigan");
assert_eq!(GenericDownloadType::HifiGanRocketVc.to_str(), "hifigan_rocket_vc");
assert_eq!(GenericDownloadType::HifiGanSoVitsSvc.to_str(), "hifigan_so_vits_svc");
assert_eq!(GenericDownloadType::RocketVc.to_str(), "rocket_vc");
assert_eq!(GenericDownloadType::RvcV2.to_str(), "rvc_v2");
assert_eq!(GenericDownloadType::SoVitsSvc.to_str(), "so_vits_svc");
assert_eq!(GenericDownloadType::Tacotron2.to_str(), "tacotron2");
assert_eq!(GenericDownloadType::Vits.to_str(), "vits");
}
#[test]
fn from_str() {
assert_eq!(GenericDownloadType::from_str("hifigan").unwrap(), GenericDownloadType::HifiGan);
assert_eq!(GenericDownloadType::from_str("hifigan_rocket_vc").unwrap(), GenericDownloadType::HifiGanRocketVc);
assert_eq!(GenericDownloadType::from_str("hifigan_so_vits_svc").unwrap(), GenericDownloadType::HifiGanSoVitsSvc);
assert_eq!(GenericDownloadType::from_str("rocket_vc").unwrap(), GenericDownloadType::RocketVc);
assert_eq!(GenericDownloadType::from_str("rvc_v2").unwrap(), GenericDownloadType::RvcV2);
assert_eq!(GenericDownloadType::from_str("so_vits_svc").unwrap(), GenericDownloadType::SoVitsSvc);
assert_eq!(GenericDownloadType::from_str("tacotron2").unwrap(), GenericDownloadType::Tacotron2);
assert_eq!(GenericDownloadType::from_str("vits").unwrap(), GenericDownloadType::Vits);
}
#[test]
fn all_variants() {
// Static check
let mut variants = GenericDownloadType::all_variants();
assert_eq!(variants.len(), 8);
assert_eq!(variants.pop_first(), Some(GenericDownloadType::HifiGan));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::HifiGanRocketVc));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::HifiGanSoVitsSvc));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::RocketVc));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::RvcV2));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::SoVitsSvc));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::Tacotron2));
assert_eq!(variants.pop_first(), Some(GenericDownloadType::Vits));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(GenericDownloadType::all_variants().len(), GenericDownloadType::iter().len());
}
}
@@ -1 +0,0 @@
pub mod generic_download_type;
@@ -1,265 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `frontend_failure_category`.
///
/// When jobs fail (permanently or transiently), we need to inform the frontend of the reason,
/// because perhaps there's something the user can do to change their input.
///
/// The previous "VARCHAR(32) failure_reason" column was a text-based message that could not be
/// localized or made user friendly. This `frontend_failure_category` exists to provide well-defined
/// failure categories to the frontend that can easily be localized and indicated consistently in
/// the UI.
///
/// Another benefit is that we'll surface all of the various types of failure and perhaps eventually
/// come to handle some in a cross-cutting way.
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
pub enum FrontendFailureCategory {
/// When a face is not detected in the image used for animation.
/// For SadTalker (and possibly Wav2Lip)
#[serde(rename = "face_not_detected")]
FaceNotDetected,
/// The user stepped away from their device and expected the workload to finish.
/// Some workloads require that the user keep their browser open.
#[serde(rename = "keep_alive_elapsed")]
KeepAliveElapsed,
/// This is mostly for developers -- a feature isn't complete somewhere in the code.
/// Big oops if errors of this class make it to production.
#[serde(rename = "not_yet_implemented")]
NotYetImplemented,
/// Tell the user that some kind of transient error happened. They don't need to know
/// exactly what happened. We'll retry their workload in any case.
#[serde(rename = "retryable_worker_error")]
RetryableWorkerError,
/// Model content rules were violated
/// Eg. Seedance 2 will report: "your input text violates platform rules. please modify and try again"
#[serde(rename = "model_rules_violation")]
ModelRulesViolation,
/// Model content violation
/// e.g. "Your uploaded image violates platform rules. Please modify and try again." (seedance2pro)
/// Model content rules prohibit the uploaded image.
#[serde(rename = "rule_bans_user_image")]
RuleBansUserImage,
/// Model content violation
/// Model content rules prohibit user uploaded images containing faces (Seedance 2.0)
/// e.g. "The generated video did not pass review. Credits will not be deducted." (seedance2pro)
#[serde(rename = "rule_bans_user_image_with_faces")]
RuleBansUserImageWithFaces,
/// Model content violation
/// Model content rules prohibit the user's given text prompt (this fails early).
/// e.g. "The generated video did not pass review. Credits will not be deducted." (seedance2pro)
#[serde(rename = "rule_bans_user_text_prompt")]
RuleBansUserTextPrompt,
/// Model content violation
/// Model content rules prohibit user content. (I think this check happens early.)
/// e.g. "Content violates platform rules. Please modify and try again." (seedance2pro)
#[serde(rename = "rule_bans_user_content")]
RuleBansUserContent,
/// Model content violation
/// The video didn't pass checks after it finished generation (this fails at the very end of the generation).
/// e.g. "The generated video did not pass review. Credits will not be deducted." (seedance2pro)
#[serde(rename = "rule_bans_generated_video")]
RuleBansGeneratedVideo,
/// Model content violation
/// The audio (even in video!) didn't pass checks after it finished generation (this fails at the very end of the generation).
/// e.g. "The generated audio violates platform rules. Please adjust your prompt or images and try again." (seedance2pro)
#[serde(rename = "rule_bans_generated_audio")]
RuleBansGeneratedAudio,
/// Model content violation
/// The content didn't pass checks after it finished generation (this fails at the very end of the generation).
/// e.g. "The generated content violates platform rules. Please adjust your prompt or images and try again." (seedance2pro)
#[serde(rename = "rule_bans_generated_content")]
RuleBansGeneratedContent,
/// Generation failed (no reason)
/// Unspecified failure reason
/// Various example failures:
/// - "The generated video did not pass review. Credits will not be deducted." (seedance2pro)
/// - "Server error. Please try again later." (seedance2pro)
#[serde(rename = "generation_failed")]
GenerationFailed,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(FrontendFailureCategory);
impl_mysql_enum_coders!(FrontendFailureCategory);
/// NB: Legacy API for older code.
impl FrontendFailureCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::FaceNotDetected => "face_not_detected",
Self::KeepAliveElapsed => "keep_alive_elapsed",
Self::NotYetImplemented => "not_yet_implemented",
Self::RetryableWorkerError => "retryable_worker_error",
Self::ModelRulesViolation => "model_rules_violation",
Self::RuleBansUserImage => "rule_bans_user_image",
Self::RuleBansUserImageWithFaces => "rule_bans_user_image_with_faces",
Self::RuleBansUserTextPrompt => "rule_bans_user_text_prompt",
Self::RuleBansUserContent => "rule_bans_user_content",
Self::RuleBansGeneratedVideo => "rule_bans_generated_video",
Self::RuleBansGeneratedAudio => "rule_bans_generated_audio",
Self::RuleBansGeneratedContent => "rule_bans_generated_content",
Self::GenerationFailed => "generation_failed",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"face_not_detected" => Ok(Self::FaceNotDetected),
"keep_alive_elapsed" => Ok(Self::KeepAliveElapsed),
"not_yet_implemented" => Ok(Self::NotYetImplemented),
"retryable_worker_error" => Ok(Self::RetryableWorkerError),
"model_rules_violation" => Ok(Self::ModelRulesViolation),
"rule_bans_user_image" => Ok(Self::RuleBansUserImage),
"rule_bans_user_image_with_faces" => Ok(Self::RuleBansUserImageWithFaces),
"rule_bans_user_text_prompt" => Ok(Self::RuleBansUserTextPrompt),
"rule_bans_user_content" => Ok(Self::RuleBansUserContent),
"rule_bans_generated_video" => Ok(Self::RuleBansGeneratedVideo),
"rule_bans_generated_audio" => Ok(Self::RuleBansGeneratedAudio),
"rule_bans_generated_content" => Ok(Self::RuleBansGeneratedContent),
"generation_failed" => Ok(Self::GenerationFailed),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::FaceNotDetected,
Self::KeepAliveElapsed,
Self::NotYetImplemented,
Self::RetryableWorkerError,
Self::ModelRulesViolation,
Self::RuleBansUserImage,
Self::RuleBansUserImageWithFaces,
Self::RuleBansUserTextPrompt,
Self::RuleBansUserContent,
Self::RuleBansGeneratedVideo,
Self::RuleBansGeneratedAudio,
Self::RuleBansGeneratedContent,
Self::GenerationFailed,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(FrontendFailureCategory::FaceNotDetected, "face_not_detected");
assert_serialization(FrontendFailureCategory::KeepAliveElapsed, "keep_alive_elapsed");
assert_serialization(FrontendFailureCategory::NotYetImplemented, "not_yet_implemented");
assert_serialization(FrontendFailureCategory::RetryableWorkerError, "retryable_worker_error");
assert_serialization(FrontendFailureCategory::ModelRulesViolation, "model_rules_violation");
assert_serialization(FrontendFailureCategory::RuleBansUserImage, "rule_bans_user_image");
assert_serialization(FrontendFailureCategory::RuleBansUserImageWithFaces, "rule_bans_user_image_with_faces");
assert_serialization(FrontendFailureCategory::RuleBansUserTextPrompt, "rule_bans_user_text_prompt");
assert_serialization(FrontendFailureCategory::RuleBansUserContent, "rule_bans_user_content");
assert_serialization(FrontendFailureCategory::RuleBansGeneratedVideo, "rule_bans_generated_video");
assert_serialization(FrontendFailureCategory::RuleBansGeneratedAudio, "rule_bans_generated_audio");
assert_serialization(FrontendFailureCategory::RuleBansGeneratedContent, "rule_bans_generated_content");
assert_serialization(FrontendFailureCategory::GenerationFailed, "generation_failed");
}
#[test]
fn to_str() {
assert_eq!(FrontendFailureCategory::FaceNotDetected.to_str(), "face_not_detected");
assert_eq!(FrontendFailureCategory::KeepAliveElapsed.to_str(), "keep_alive_elapsed");
assert_eq!(FrontendFailureCategory::NotYetImplemented.to_str(), "not_yet_implemented");
assert_eq!(FrontendFailureCategory::RetryableWorkerError.to_str(), "retryable_worker_error");
assert_eq!(FrontendFailureCategory::ModelRulesViolation.to_str(), "model_rules_violation");
assert_eq!(FrontendFailureCategory::RuleBansUserImage.to_str(), "rule_bans_user_image");
assert_eq!(FrontendFailureCategory::RuleBansUserImageWithFaces.to_str(), "rule_bans_user_image_with_faces");
assert_eq!(FrontendFailureCategory::RuleBansUserTextPrompt.to_str(), "rule_bans_user_text_prompt");
assert_eq!(FrontendFailureCategory::RuleBansUserContent.to_str(), "rule_bans_user_content");
assert_eq!(FrontendFailureCategory::RuleBansGeneratedVideo.to_str(), "rule_bans_generated_video");
assert_eq!(FrontendFailureCategory::RuleBansGeneratedAudio.to_str(), "rule_bans_generated_audio");
assert_eq!(FrontendFailureCategory::RuleBansGeneratedContent.to_str(), "rule_bans_generated_content");
assert_eq!(FrontendFailureCategory::GenerationFailed.to_str(), "generation_failed");
}
#[test]
fn from_str() {
assert_eq!(FrontendFailureCategory::from_str("face_not_detected").unwrap(), FrontendFailureCategory::FaceNotDetected);
assert_eq!(FrontendFailureCategory::from_str("keep_alive_elapsed").unwrap(), FrontendFailureCategory::KeepAliveElapsed);
assert_eq!(FrontendFailureCategory::from_str("not_yet_implemented").unwrap(), FrontendFailureCategory::NotYetImplemented);
assert_eq!(FrontendFailureCategory::from_str("retryable_worker_error").unwrap(), FrontendFailureCategory::RetryableWorkerError);
assert_eq!(FrontendFailureCategory::from_str("model_rules_violation").unwrap(), FrontendFailureCategory::ModelRulesViolation);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_user_image").unwrap(), FrontendFailureCategory::RuleBansUserImage);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_user_image_with_faces").unwrap(), FrontendFailureCategory::RuleBansUserImageWithFaces);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_user_text_prompt").unwrap(), FrontendFailureCategory::RuleBansUserTextPrompt);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_user_content").unwrap(), FrontendFailureCategory::RuleBansUserContent);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_generated_video").unwrap(), FrontendFailureCategory::RuleBansGeneratedVideo);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_generated_audio").unwrap(), FrontendFailureCategory::RuleBansGeneratedAudio);
assert_eq!(FrontendFailureCategory::from_str("rule_bans_generated_content").unwrap(), FrontendFailureCategory::RuleBansGeneratedContent);
assert_eq!(FrontendFailureCategory::from_str("generation_failed").unwrap(), FrontendFailureCategory::GenerationFailed);
assert_eq!(FrontendFailureCategory::from_str("invalid_value").is_err(), true);
}
#[test]
fn all_variants() {
let mut variants = FrontendFailureCategory::all_variants();
assert_eq!(variants.len(), 13);
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::FaceNotDetected));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::KeepAliveElapsed));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::NotYetImplemented));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RetryableWorkerError));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::ModelRulesViolation));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansUserImage));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansUserImageWithFaces));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansUserTextPrompt));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansUserContent));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansGeneratedVideo));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansGeneratedAudio));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::RuleBansGeneratedContent));
assert_eq!(variants.pop_first(), Some(FrontendFailureCategory::GenerationFailed));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(FrontendFailureCategory::all_variants().len(), FrontendFailureCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in FrontendFailureCategory::all_variants() {
assert_eq!(variant, FrontendFailureCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, FrontendFailureCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, FrontendFailureCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1,285 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `inference_category`.
///
/// Our "generic inference" pipeline supports a wide variety of ML models and other media.
/// Each "category" of inference is identified by the following enum variants.
/// These types are present in the HTTP API and database columns as serialized here.
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, Default, ToSchema)]
pub enum InferenceCategory {
/// Deprecate this field !!!
/// We should drain all jobs from using this database field, then remove it.
#[deprecated(note = "NB(bt,2024-09-05): The frontend still needs this")]
#[serde(rename = "deprecated_field")]
DeprecatedField,
/// Facial lipsync animation (eg. SadTalker, Wav2Lip, FaceFusion)
#[serde(rename = "lipsync_animation")]
#[default]
LipsyncAnimation,
/// FakeYou's text to speech (eg. Tacotron2)
#[serde(rename = "text_to_speech")]
TextToSpeech,
/// FakeYou's voice conversion (eg. svc, rvc)
#[serde(rename = "voice_conversion")]
VoiceConversion,
/// Image generation (eg. Stable Diffusion 1.5), FAL-powered image generation, etc.
#[serde(rename = "image_generation")]
ImageGeneration,
/// FAL-powered video generation
/// (Also Seedance2-Pro.com)
#[serde(rename = "video_generation")]
VideoGeneration,
/// FAL-powered 3D object generation
#[serde(rename = "object_generation")]
ObjectGeneration,
/// Gaussian Splat generation (eg. World Labs Marble)
#[serde(rename = "splat_generation")]
SplatGeneration,
/// FAL-powered image background removal
#[serde(rename = "background_removal")]
BackgroundRemoval,
/// Turn video into animation data with mocap processing (eg. Mocapnet).
#[serde(rename = "mocap")]
Mocap,
/// ComfyUI workflows
/// This is what powers Storyteller Studio!
#[serde(rename = "workflow")]
Workflow,
/// FBX to GLTF/GLB.
/// Still supported, but few people will use it.
#[serde(rename = "format_conversion")]
FormatConversion,
/// Live portrait
#[serde(rename = "live_portrait")]
LivePortrait,
#[serde(rename="seed_vc")]
SeedVc,
/// DEPRECATED. Do not use.
/// This was for ReRenderAVideo, which we never productionized.
#[deprecated(note = "This was for ReRenderAVideo, which we never productionized.")]
#[serde(rename = "video_filter")]
VideoFilter,
/// DEPRECATED. Bevy engine serverside rendering.
#[deprecated(note = "This was for Bevy engine's server side rendering.")]
#[serde(rename = "convert_bvh_to_workflow")]
ConvertBvhToWorkflow,
#[serde(rename = "f5_tts")]
F5TTS,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(InferenceCategory);
impl_mysql_enum_coders!(InferenceCategory);
impl_mysql_from_row!(InferenceCategory);
/// NB: Legacy API for older code.
impl InferenceCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::DeprecatedField => "deprecated_field",
Self::LipsyncAnimation => "lipsync_animation",
Self::TextToSpeech => "text_to_speech",
Self::VoiceConversion => "voice_conversion",
Self::ImageGeneration => "image_generation",
Self::VideoGeneration => "video_generation",
Self::ObjectGeneration => "object_generation",
Self::SplatGeneration => "splat_generation",
Self::BackgroundRemoval => "background_removal",
Self::Mocap => "mocap",
Self::Workflow => "workflow",
Self::F5TTS => "f5_tts",
Self::FormatConversion => "format_conversion",
Self::LivePortrait => "live_portrait",
Self::SeedVc => "seed_vc",
Self::VideoFilter => "video_filter",
Self::ConvertBvhToWorkflow => "convert_bvh_to_workflow",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"deprecated_field" => Ok(Self::DeprecatedField),
"lipsync_animation" => Ok(Self::LipsyncAnimation),
"text_to_speech" => Ok(Self::TextToSpeech),
"voice_conversion" => Ok(Self::VoiceConversion),
"image_generation" => Ok(Self::ImageGeneration),
"video_generation" => Ok(Self::VideoGeneration),
"object_generation" => Ok(Self::ObjectGeneration),
"splat_generation" => Ok(Self::SplatGeneration),
"background_removal" => Ok(Self::BackgroundRemoval),
"f5_tts" => Ok(Self::F5TTS),
"mocap" => Ok(Self::Mocap),
"workflow" => Ok(Self::Workflow),
"format_conversion" => Ok(Self::FormatConversion),
"live_portrait" => Ok(Self::LivePortrait),
"seed_vc" => Ok(Self::SeedVc),
"video_filter" => Ok(Self::VideoFilter),
"convert_bvh_to_workflow" => Ok(Self::ConvertBvhToWorkflow),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::DeprecatedField,
Self::LipsyncAnimation,
Self::TextToSpeech,
Self::VoiceConversion,
Self::ImageGeneration,
Self::ObjectGeneration,
Self::SplatGeneration,
Self::VideoGeneration,
Self::BackgroundRemoval,
Self::Mocap,
Self::F5TTS,
Self::SeedVc,
Self::Workflow,
Self::FormatConversion,
Self::LivePortrait,
Self::VideoFilter,
Self::ConvertBvhToWorkflow,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::inference_category::InferenceCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(InferenceCategory::DeprecatedField, "deprecated_field");
assert_serialization(InferenceCategory::LipsyncAnimation, "lipsync_animation");
assert_serialization(InferenceCategory::TextToSpeech, "text_to_speech");
assert_serialization(InferenceCategory::VoiceConversion, "voice_conversion");
assert_serialization(InferenceCategory::ImageGeneration, "image_generation");
assert_serialization(InferenceCategory::VideoGeneration, "video_generation");
assert_serialization(InferenceCategory::ObjectGeneration, "object_generation");
assert_serialization(InferenceCategory::SplatGeneration, "splat_generation");
assert_serialization(InferenceCategory::BackgroundRemoval, "background_removal");
assert_serialization(InferenceCategory::Mocap, "mocap");
assert_serialization(InferenceCategory::F5TTS, "f5_tts");
assert_serialization(InferenceCategory::SeedVc, "seed_vc");
assert_serialization(InferenceCategory::Workflow, "workflow");
assert_serialization(InferenceCategory::FormatConversion, "format_conversion");
assert_serialization(InferenceCategory::LivePortrait, "live_portrait");
assert_serialization(InferenceCategory::VideoFilter, "video_filter");
assert_serialization(InferenceCategory::ConvertBvhToWorkflow, "convert_bvh_to_workflow");
}
#[test]
fn to_str() {
assert_eq!(InferenceCategory::DeprecatedField.to_str(), "deprecated_field");
assert_eq!(InferenceCategory::LipsyncAnimation.to_str(), "lipsync_animation");
assert_eq!(InferenceCategory::TextToSpeech.to_str(), "text_to_speech");
assert_eq!(InferenceCategory::VoiceConversion.to_str(), "voice_conversion");
assert_eq!(InferenceCategory::ImageGeneration.to_str(), "image_generation");
assert_eq!(InferenceCategory::VideoGeneration.to_str(), "video_generation");
assert_eq!(InferenceCategory::ObjectGeneration.to_str(), "object_generation");
assert_eq!(InferenceCategory::SplatGeneration.to_str(), "splat_generation");
assert_eq!(InferenceCategory::BackgroundRemoval.to_str(), "background_removal");
assert_eq!(InferenceCategory::F5TTS.to_str(), "f5_tts");
assert_eq!(InferenceCategory::SeedVc.to_str(), "seed_vc");
assert_eq!(InferenceCategory::Mocap.to_str(), "mocap");
assert_eq!(InferenceCategory::Workflow.to_str(), "workflow");
assert_eq!(InferenceCategory::FormatConversion.to_str(), "format_conversion");
assert_eq!(InferenceCategory::LivePortrait.to_str(), "live_portrait");
assert_eq!(InferenceCategory::VideoFilter.to_str(), "video_filter");
assert_eq!(InferenceCategory::ConvertBvhToWorkflow.to_str(), "convert_bvh_to_workflow");
}
#[test]
fn from_str() {
assert_eq!(InferenceCategory::from_str("deprecated_field").unwrap(), InferenceCategory::DeprecatedField);
assert_eq!(InferenceCategory::from_str("lipsync_animation").unwrap(), InferenceCategory::LipsyncAnimation);
assert_eq!(InferenceCategory::from_str("text_to_speech").unwrap(), InferenceCategory::TextToSpeech);
assert_eq!(InferenceCategory::from_str("voice_conversion").unwrap(), InferenceCategory::VoiceConversion);
assert_eq!(InferenceCategory::from_str("image_generation").unwrap(), InferenceCategory::ImageGeneration);
assert_eq!(InferenceCategory::from_str("video_generation").unwrap(), InferenceCategory::VideoGeneration);
assert_eq!(InferenceCategory::from_str("object_generation").unwrap(), InferenceCategory::ObjectGeneration);
assert_eq!(InferenceCategory::from_str("splat_generation").unwrap(), InferenceCategory::SplatGeneration);
assert_eq!(InferenceCategory::from_str("background_removal").unwrap(), InferenceCategory::BackgroundRemoval);
assert_eq!(InferenceCategory::from_str("f5_tts").unwrap(), InferenceCategory::F5TTS);
assert_eq!(InferenceCategory::from_str("seed_vc").unwrap(), InferenceCategory::SeedVc);
assert_eq!(InferenceCategory::from_str("mocap").unwrap(), InferenceCategory::Mocap);
assert_eq!(InferenceCategory::from_str("workflow").unwrap(), InferenceCategory::Workflow);
assert_eq!(InferenceCategory::from_str("format_conversion").unwrap(), InferenceCategory::FormatConversion);
assert_eq!(InferenceCategory::from_str("live_portrait").unwrap(), InferenceCategory::LivePortrait);
assert_eq!(InferenceCategory::from_str("video_filter").unwrap(), InferenceCategory::VideoFilter);
assert_eq!(InferenceCategory::from_str("convert_bvh_to_workflow").unwrap(), InferenceCategory::ConvertBvhToWorkflow);
}
#[test]
fn all_variants() {
// Static check
const EXPECTED_COUNT : usize = 17;
assert_eq!(InferenceCategory::all_variants().len(), EXPECTED_COUNT);
assert_eq!(InferenceCategory::iter().len(), EXPECTED_COUNT);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(InferenceCategory::all_variants().len(), InferenceCategory::iter().len());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(InferenceCategory::all_variants().len(), InferenceCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in InferenceCategory::all_variants() {
assert_eq!(variant, InferenceCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, InferenceCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, InferenceCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in InferenceCategory::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);
}
}
}
}
@@ -1,87 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `maybe_input_source_token`.
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum InferenceInputSourceTokenType {
#[serde(rename = "media_file")]
MediaFile,
#[serde(rename = "media_upload")]
MediaUpload,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(InferenceInputSourceTokenType);
impl_mysql_enum_coders!(InferenceInputSourceTokenType);
/// NB: Legacy API for older code.
impl InferenceInputSourceTokenType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::MediaUpload => "media_upload",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"media_upload" => Ok(Self::MediaUpload),
_ => Err(format!("invalid value: {:?}", value)),
}
}
pub fn all_variants() -> BTreeSet<InferenceInputSourceTokenType> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
InferenceInputSourceTokenType::MediaFile,
InferenceInputSourceTokenType::MediaUpload,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::inference_input_source_token_type::InferenceInputSourceTokenType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(InferenceInputSourceTokenType::MediaFile, "media_file");
assert_serialization(InferenceInputSourceTokenType::MediaUpload, "media_upload");
}
#[test]
fn to_str() {
assert_eq!(InferenceInputSourceTokenType::MediaFile.to_str(), "media_file");
assert_eq!(InferenceInputSourceTokenType::MediaUpload.to_str(), "media_upload");
}
#[test]
fn from_str() {
assert_eq!(InferenceInputSourceTokenType::from_str("media_file").unwrap(), InferenceInputSourceTokenType::MediaFile);
assert_eq!(InferenceInputSourceTokenType::from_str("media_upload").unwrap(), InferenceInputSourceTokenType::MediaUpload);
}
#[test]
fn all_variants() {
// Static check
let mut variants = InferenceInputSourceTokenType::all_variants();
assert_eq!(variants.len(), 2);
assert_eq!(variants.pop_first(), Some(InferenceInputSourceTokenType::MediaFile));
assert_eq!(variants.pop_first(), Some(InferenceInputSourceTokenType::MediaUpload));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(InferenceInputSourceTokenType::all_variants().len(), InferenceInputSourceTokenType::iter().len());
}
}
@@ -1,134 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `generic_inference_jobs` table in `VARCHAR(16)` field `maybe_external_third_party`.
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum InferenceJobExternalThirdParty {
/// Fal jobs
#[serde(rename = "fal")]
#[default]
Fal,
/// Seedance 2 Pro jobs
#[serde(rename = "seedance2pro")]
Seedance2Pro,
/// World Labs jobs
#[serde(rename = "worldlabs")]
Worldlabs,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(InferenceJobExternalThirdParty);
impl_mysql_enum_coders!(InferenceJobExternalThirdParty);
/// NB: Legacy API for older code.
impl InferenceJobExternalThirdParty {
pub fn to_str(&self) -> &'static str {
match self {
Self::Fal => "fal",
Self::Seedance2Pro => "seedance2pro",
Self::Worldlabs => "worldlabs",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"fal" => Ok(Self::Fal),
"seedance2pro" => Ok(Self::Seedance2Pro),
"worldlabs" => Ok(Self::Worldlabs),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Fal,
Self::Seedance2Pro,
Self::Worldlabs,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::inference_job_external_third_party::InferenceJobExternalThirdParty;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(InferenceJobExternalThirdParty::Fal, "fal");
assert_serialization(InferenceJobExternalThirdParty::Seedance2Pro, "seedance2pro");
assert_serialization(InferenceJobExternalThirdParty::Worldlabs, "worldlabs");
}
#[test]
fn to_str() {
assert_eq!(InferenceJobExternalThirdParty::Fal.to_str(), "fal");
assert_eq!(InferenceJobExternalThirdParty::Seedance2Pro.to_str(), "seedance2pro");
assert_eq!(InferenceJobExternalThirdParty::Worldlabs.to_str(), "worldlabs");
}
#[test]
fn from_str() {
assert_eq!(InferenceJobExternalThirdParty::from_str("fal").unwrap(), InferenceJobExternalThirdParty::Fal);
assert_eq!(InferenceJobExternalThirdParty::from_str("seedance2pro").unwrap(), InferenceJobExternalThirdParty::Seedance2Pro);
assert_eq!(InferenceJobExternalThirdParty::from_str("worldlabs").unwrap(), InferenceJobExternalThirdParty::Worldlabs);
}
#[test]
fn all_variants() {
// Static check
const EXPECTED_COUNT : usize = 3;
assert_eq!(InferenceJobExternalThirdParty::all_variants().len(), EXPECTED_COUNT);
assert_eq!(InferenceJobExternalThirdParty::iter().len(), EXPECTED_COUNT);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(InferenceJobExternalThirdParty::all_variants().len(), InferenceJobExternalThirdParty::iter().len());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(InferenceJobExternalThirdParty::all_variants().len(), InferenceJobExternalThirdParty::iter().len());
}
#[test]
fn round_trip() {
for variant in InferenceJobExternalThirdParty::all_variants() {
assert_eq!(variant, InferenceJobExternalThirdParty::from_str(variant.to_str()).unwrap());
assert_eq!(variant, InferenceJobExternalThirdParty::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, InferenceJobExternalThirdParty::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in InferenceJobExternalThirdParty::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);
}
}
}
}
@@ -1,385 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `product_category`.
///
/// This is a user-facing and analytics-facing column that describes what product area the job
/// is attributed to. For example, this will help us separate "video style transfer" from
/// "storyteller studio" and also separate "live portrait" from "webcam live portrait".
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum InferenceJobProductCategory {
// =============== DOWNLOAD ===============
/// Download: GptSoVits
#[default]
DownloadGptSoVits,
// =============== FAL ===============
FalImage,
FalVideo,
/// Fal: 3D Object Generation
FalObject,
FalBgRemoval,
// =============== SEEDANCE 2 PRO ===============
#[serde(rename = "seedance2pro_video")]
Seedance2ProVideo,
// =============== WORLD LABS ===============
/// World Labs: Gaussian Splat Generation
#[serde(rename = "worldlabs_splat")]
WorldlabsSplat,
// =============== TEXT TO SPEECH ===============
/// TTS: GptSoVits
TtsGptSoVits,
/// TTS: F5Tts (Zero Shot)
TtsF5,
/// TTS: StyleTts2 (Zero Shot)
TtsStyleTts2,
/// TTS: Tacotron2
TtsTacotron2,
// =============== VOICE CONVERSION ===============
/// Voice Conversion: RVC v2
VcRvc2,
/// Voice Conversion: SoVitsSvc
VcSvc,
VcSeedVc, // Ugh
// =============== VIDEO ===============
/// Video: Face Fusion (Lipsync)
VidLipsyncFaceFusion,
/// Video: Sad Talker (Lipsync)
VidLipsyncSadTalker,
/// Live Portrait (normal interface)
VidLivePortrait,
/// Live Portrait (webcam interface)
VidLivePortraitWebcam,
/// Video: Studio
VidStudio,
/// Video: Studio Gen 2
VidStudioGen2,
/// Video: Style Transfer
VidStyleTransfer,
// =============== DEPRECATED ===============
/// Lipsync: Face Fusion
#[deprecated(note = "Use `VidLipsyncFaceFusion` instead")]
LipsyncFaceFusion,
/// Lipsync: SadTalker
#[deprecated(note = "Use `VidLipsyncSadTalker` instead")]
LipsyncSadTalker,
/// Live Portrait (normal interface)
#[deprecated(note = "Use `VidLivePortrait` instead")]
LivePortrait,
/// Live Portrait (webcam interface)
#[deprecated(note = "Use `VidLivePortraitWebcam` instead")]
LivePortraitWebcam,
/// Stable Diffusion (deprecated)
#[deprecated(note = "unused")]
StableDiffusion,
/// Storyteller Studio
#[deprecated(note = "Use `VidStudio` instead")]
Studio,
/// Lipsync: Face Fusion
#[deprecated(note = "Use `VidLipsyncFaceFusion` instead")]
VidFaceFusion,
/// Video Style Transfer
#[deprecated(note = "Use `VidStyleTransfer` instead")]
Vst,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(InferenceJobProductCategory);
impl_mysql_enum_coders!(InferenceJobProductCategory);
/// NB: Legacy API for older code.
impl InferenceJobProductCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::DownloadGptSoVits => "download_gpt_so_vits",
Self::FalImage => "fal_image",
Self::FalVideo => "fal_video",
Self::FalObject => "fal_object",
Self::FalBgRemoval => "fal_bg_removal",
Self::Seedance2ProVideo => "seedance2pro_video",
Self::WorldlabsSplat => "worldlabs_splat",
Self::TtsGptSoVits => "tts_gpt_so_vits",
Self::TtsStyleTts2 => "tts_style_tts2",
Self::TtsTacotron2 => "tts_tacotron2",
Self::TtsF5 => "tts_f5",
Self::VcSvc => "vc_svc",
Self::VcRvc2 => "vc_rvc2",
Self::VcSeedVc => "vc_seed_vc",
Self::VidLipsyncFaceFusion => "vid_lipsync_face_fusion",
Self::VidLipsyncSadTalker => "vid_lipsync_sad_talker",
Self::VidLivePortrait => "vid_live_portrait",
Self::VidLivePortraitWebcam => "vid_live_portrait_webcam",
Self::VidStudio => "vid_studio",
Self::VidStudioGen2 => "vid_studio_gen2",
Self::VidStyleTransfer => "vid_style_transfer",
Self::LipsyncFaceFusion => "lipsync_face_fusion",
Self::LipsyncSadTalker => "lipsync_sad_talker",
Self::LivePortrait => "live_portrait",
Self::LivePortraitWebcam => "live_portrait_webcam",
Self::StableDiffusion => "stable_diffusion",
Self::Studio => "studio",
Self::VidFaceFusion => "vid_face_fusion",
Self::Vst => "vst",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"download_gpt_so_vits" => Ok(Self::DownloadGptSoVits),
"fal_image" => Ok(Self::FalImage),
"fal_video" => Ok(Self::FalVideo),
"fal_object" => Ok(Self::FalObject),
"fal_bg_removal" => Ok(Self::FalBgRemoval),
"seedance2pro_video" => Ok(Self::Seedance2ProVideo),
"worldlabs_splat" => Ok(Self::WorldlabsSplat),
"tts_gpt_so_vits" => Ok(Self::TtsGptSoVits),
"tts_style_tts2" => Ok(Self::TtsStyleTts2),
"tts_tacotron2" => Ok(Self::TtsTacotron2),
"tts_f5" => Ok(Self::TtsF5),
"vc_svc" => Ok(Self::VcSvc),
"vc_rvc2" => Ok(Self::VcRvc2),
"vc_seed_vc" => Ok(Self::VcSeedVc),
"vid_lipsync_face_fusion" => Ok(Self::VidLipsyncFaceFusion),
"vid_lipsync_sad_talker" => Ok(Self::VidLipsyncSadTalker),
"vid_live_portrait" => Ok(Self::VidLivePortrait),
"vid_live_portrait_webcam" => Ok(Self::VidLivePortraitWebcam),
"vid_studio" => Ok(Self::VidStudio),
"vid_studio_gen2" => Ok(Self::VidStudioGen2),
"vid_style_transfer" => Ok(Self::VidStyleTransfer),
"lipsync_face_fusion" => Ok(Self::LipsyncFaceFusion),
"lipsync_sad_talker" => Ok(Self::LipsyncSadTalker),
"live_portrait" => Ok(Self::LivePortrait),
"live_portrait_webcam" => Ok(Self::LivePortraitWebcam),
"stable_diffusion" => Ok(Self::StableDiffusion),
"studio" => Ok(Self::Studio),
"vid_face_fusion" => Ok(Self::VidFaceFusion),
"vst" => Ok(Self::Vst),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::DownloadGptSoVits,
Self::FalImage,
Self::FalVideo,
Self::FalObject,
Self::FalBgRemoval,
Self::Seedance2ProVideo,
Self::WorldlabsSplat,
Self::TtsGptSoVits,
Self::TtsStyleTts2,
Self::TtsTacotron2,
Self::TtsF5,
Self::VcSeedVc,
Self::VcSvc,
Self::VcRvc2,
Self::VidLipsyncFaceFusion,
Self::VidLipsyncSadTalker,
Self::VidLivePortrait,
Self::VidLivePortraitWebcam,
Self::VidStudio,
Self::VidStudioGen2,
Self::VidStyleTransfer,
Self::LipsyncFaceFusion,
Self::LipsyncSadTalker,
Self::LivePortrait,
Self::LivePortraitWebcam,
Self::StableDiffusion,
Self::Studio,
Self::VidFaceFusion,
Self::Vst,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::inference_job_product_category::InferenceJobProductCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(InferenceJobProductCategory::DownloadGptSoVits, "download_gpt_so_vits");
assert_serialization(InferenceJobProductCategory::FalImage, "fal_image");
assert_serialization(InferenceJobProductCategory::FalVideo, "fal_video");
assert_serialization(InferenceJobProductCategory::FalObject, "fal_object");
assert_serialization(InferenceJobProductCategory::FalBgRemoval, "fal_bg_removal");
assert_serialization(InferenceJobProductCategory::Seedance2ProVideo, "seedance2pro_video");
assert_serialization(InferenceJobProductCategory::WorldlabsSplat, "worldlabs_splat");
assert_serialization(InferenceJobProductCategory::TtsGptSoVits, "tts_gpt_so_vits");
assert_serialization(InferenceJobProductCategory::TtsStyleTts2, "tts_style_tts2");
assert_serialization(InferenceJobProductCategory::TtsTacotron2, "tts_tacotron2");
assert_serialization(InferenceJobProductCategory::TtsF5, "tts_f5");
assert_serialization(InferenceJobProductCategory::VcRvc2, "vc_rvc2");
assert_serialization(InferenceJobProductCategory::VcSvc, "vc_svc");
assert_serialization(InferenceJobProductCategory::VcSvc, "vc_svc");
assert_serialization(InferenceJobProductCategory::VidLipsyncFaceFusion, "vid_lipsync_face_fusion");
assert_serialization(InferenceJobProductCategory::VidLipsyncSadTalker, "vid_lipsync_sad_talker");
assert_serialization(InferenceJobProductCategory::VidLivePortrait, "vid_live_portrait");
assert_serialization(InferenceJobProductCategory::VidLivePortraitWebcam, "vid_live_portrait_webcam");
assert_serialization(InferenceJobProductCategory::VidStudio, "vid_studio");
assert_serialization(InferenceJobProductCategory::VidStudioGen2, "vid_studio_gen2");
assert_serialization(InferenceJobProductCategory::VidStyleTransfer, "vid_style_transfer");
assert_serialization(InferenceJobProductCategory::LipsyncFaceFusion, "lipsync_face_fusion");
assert_serialization(InferenceJobProductCategory::LipsyncSadTalker, "lipsync_sad_talker");
assert_serialization(InferenceJobProductCategory::LivePortrait, "live_portrait");
assert_serialization(InferenceJobProductCategory::LivePortraitWebcam, "live_portrait_webcam");
assert_serialization(InferenceJobProductCategory::StableDiffusion, "stable_diffusion");
assert_serialization(InferenceJobProductCategory::Studio, "studio");
assert_serialization(InferenceJobProductCategory::VidFaceFusion, "vid_face_fusion");
assert_serialization(InferenceJobProductCategory::Vst, "vst");
}
#[test]
fn to_str() {
assert_eq!(InferenceJobProductCategory::DownloadGptSoVits.to_str(), "download_gpt_so_vits");
assert_eq!(InferenceJobProductCategory::FalImage.to_str(), "fal_image");
assert_eq!(InferenceJobProductCategory::FalVideo.to_str(), "fal_video");
assert_eq!(InferenceJobProductCategory::FalObject.to_str(), "fal_object");
assert_eq!(InferenceJobProductCategory::FalBgRemoval.to_str(), "fal_bg_removal");
assert_eq!(InferenceJobProductCategory::Seedance2ProVideo.to_str(), "seedance2pro_video");
assert_eq!(InferenceJobProductCategory::WorldlabsSplat.to_str(), "worldlabs_splat");
assert_eq!(InferenceJobProductCategory::TtsGptSoVits.to_str(), "tts_gpt_so_vits");
assert_eq!(InferenceJobProductCategory::TtsStyleTts2.to_str(), "tts_style_tts2");
assert_eq!(InferenceJobProductCategory::TtsTacotron2.to_str(), "tts_tacotron2");
assert_eq!(InferenceJobProductCategory::VcRvc2.to_str(), "vc_rvc2");
assert_eq!(InferenceJobProductCategory::VcSvc.to_str(), "vc_svc");
assert_eq!(InferenceJobProductCategory::VcSvc.to_str(), "vc_svc");
assert_eq!(InferenceJobProductCategory::VidLipsyncFaceFusion.to_str(), "vid_lipsync_face_fusion");
assert_eq!(InferenceJobProductCategory::VidLipsyncSadTalker.to_str(), "vid_lipsync_sad_talker");
assert_eq!(InferenceJobProductCategory::VidLivePortrait.to_str(), "vid_live_portrait");
assert_eq!(InferenceJobProductCategory::VidLivePortraitWebcam.to_str(), "vid_live_portrait_webcam");
assert_eq!(InferenceJobProductCategory::VidStudio.to_str(), "vid_studio");
assert_eq!(InferenceJobProductCategory::VidStudioGen2.to_str(), "vid_studio_gen2");
assert_eq!(InferenceJobProductCategory::VidStyleTransfer.to_str(), "vid_style_transfer");
assert_eq!(InferenceJobProductCategory::LipsyncFaceFusion.to_str(), "lipsync_face_fusion");
assert_eq!(InferenceJobProductCategory::LipsyncSadTalker.to_str(), "lipsync_sad_talker");
assert_eq!(InferenceJobProductCategory::LivePortrait.to_str(), "live_portrait");
assert_eq!(InferenceJobProductCategory::LivePortraitWebcam.to_str(), "live_portrait_webcam");
assert_eq!(InferenceJobProductCategory::StableDiffusion.to_str(), "stable_diffusion");
assert_eq!(InferenceJobProductCategory::Studio.to_str(), "studio");
assert_eq!(InferenceJobProductCategory::VidFaceFusion.to_str(), "vid_face_fusion");
assert_eq!(InferenceJobProductCategory::Vst.to_str(), "vst");
}
#[test]
fn from_str() {
assert_eq!(InferenceJobProductCategory::from_str("download_gpt_so_vits").unwrap(), InferenceJobProductCategory::DownloadGptSoVits);
assert_eq!(InferenceJobProductCategory::from_str("fal_image").unwrap(), InferenceJobProductCategory::FalImage);
assert_eq!(InferenceJobProductCategory::from_str("fal_video").unwrap(), InferenceJobProductCategory::FalVideo);
assert_eq!(InferenceJobProductCategory::from_str("fal_object").unwrap(), InferenceJobProductCategory::FalObject);
assert_eq!(InferenceJobProductCategory::from_str("fal_bg_removal").unwrap(), InferenceJobProductCategory::FalBgRemoval);
assert_eq!(InferenceJobProductCategory::from_str("seedance2pro_video").unwrap(), InferenceJobProductCategory::Seedance2ProVideo);
assert_eq!(InferenceJobProductCategory::from_str("worldlabs_splat").unwrap(), InferenceJobProductCategory::WorldlabsSplat);
assert_eq!(InferenceJobProductCategory::from_str("tts_gpt_so_vits").unwrap(), InferenceJobProductCategory::TtsGptSoVits);
assert_eq!(InferenceJobProductCategory::from_str("tts_style_tts2").unwrap(), InferenceJobProductCategory::TtsStyleTts2);
assert_eq!(InferenceJobProductCategory::from_str("tts_tacotron2").unwrap(), InferenceJobProductCategory::TtsTacotron2);
assert_eq!(InferenceJobProductCategory::from_str("vc_rvc2").unwrap(), InferenceJobProductCategory::VcRvc2);
assert_eq!(InferenceJobProductCategory::from_str("vc_svc").unwrap(), InferenceJobProductCategory::VcSvc);
assert_eq!(InferenceJobProductCategory::from_str("vc_svc").unwrap(), InferenceJobProductCategory::VcSvc);
assert_eq!(InferenceJobProductCategory::from_str("vid_lipsync_face_fusion").unwrap(), InferenceJobProductCategory::VidLipsyncFaceFusion);
assert_eq!(InferenceJobProductCategory::from_str("vid_lipsync_sad_talker").unwrap(), InferenceJobProductCategory::VidLipsyncSadTalker);
assert_eq!(InferenceJobProductCategory::from_str("vid_live_portrait").unwrap(), InferenceJobProductCategory::VidLivePortrait);
assert_eq!(InferenceJobProductCategory::from_str("vid_live_portrait_webcam").unwrap(), InferenceJobProductCategory::VidLivePortraitWebcam);
assert_eq!(InferenceJobProductCategory::from_str("vid_studio").unwrap(), InferenceJobProductCategory::VidStudio);
assert_eq!(InferenceJobProductCategory::from_str("vid_studio_gen2").unwrap(), InferenceJobProductCategory::VidStudioGen2);
assert_eq!(InferenceJobProductCategory::from_str("vid_style_transfer").unwrap(), InferenceJobProductCategory::VidStyleTransfer);
assert_eq!(InferenceJobProductCategory::from_str("lipsync_face_fusion").unwrap(), InferenceJobProductCategory::LipsyncFaceFusion);
assert_eq!(InferenceJobProductCategory::from_str("lipsync_sad_talker").unwrap(), InferenceJobProductCategory::LipsyncSadTalker);
assert_eq!(InferenceJobProductCategory::from_str("live_portrait").unwrap(), InferenceJobProductCategory::LivePortrait);
assert_eq!(InferenceJobProductCategory::from_str("live_portrait_webcam").unwrap(), InferenceJobProductCategory::LivePortraitWebcam);
assert_eq!(InferenceJobProductCategory::from_str("stable_diffusion").unwrap(), InferenceJobProductCategory::StableDiffusion);
assert_eq!(InferenceJobProductCategory::from_str("studio").unwrap(), InferenceJobProductCategory::Studio);
assert_eq!(InferenceJobProductCategory::from_str("vid_face_fusion").unwrap(), InferenceJobProductCategory::VidFaceFusion);
assert_eq!(InferenceJobProductCategory::from_str("vst").unwrap(), InferenceJobProductCategory::Vst);
}
#[test]
fn all_variants() {
// Static check
const EXPECTED_COUNT : usize = 29;
assert_eq!(InferenceJobProductCategory::all_variants().len(), EXPECTED_COUNT);
assert_eq!(InferenceJobProductCategory::iter().len(), EXPECTED_COUNT);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(InferenceJobProductCategory::all_variants().len(), InferenceJobProductCategory::iter().len());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(InferenceJobProductCategory::all_variants().len(), InferenceJobProductCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in InferenceJobProductCategory::all_variants() {
assert_eq!(variant, InferenceJobProductCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, InferenceJobProductCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, InferenceJobProductCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in InferenceJobProductCategory::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);
}
}
}
}
@@ -1,252 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `maybe_model_type`.
///
/// Our "generic inference" pipeline supports a wide variety of ML models and other media.
/// Each inference "model type" identified by the following enum variants, though some pipelines
/// may use multiple models or no model (and may report NULL).
///
/// These types are present in the HTTP API and database columns as serialized here.
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum InferenceModelType {
// TODO(bt,2024-07-15): This is too generic. We probably need "StorytellerStudio", "LivePortrait", etc.
#[serde(rename = "comfy_ui")]
ComfyUi,
#[serde(rename = "rvc_v2")]
RvcV2,
// NB: sad_talker does use user-supplied models, so there is no "model token"
#[serde(rename = "sad_talker")]
SadTalker,
#[serde(rename = "so_vits_svc")]
SoVitsSvc,
// TODO: Does this need to be "legacy_tacotron2" ?
#[serde(rename = "seed_vc")]
SeedVc,
/// NB: This is for Sora GPT 4o image gen
#[serde(rename = "image_gen_api")]
ImageGenApi,
#[serde(rename = "tacotron2")]
Tacotron2,
#[serde(rename = "vits")]
Vits,
#[serde(rename = "vall_e_x")]
VallEX,
#[serde(rename = "rerender_a_video")]
RerenderAVideo,
#[serde(rename = "stable_diffusion")]
StableDiffusion,
#[serde(rename = "mocap_net")]
MocapNet,
#[serde(rename = "styletts2")]
StyleTTS2,
/// A job that turns "FBX" game engine files into "GLTF" files (Bevy-compatible).
#[serde(rename = "convert_fbx_gltf")]
ConvertFbxToGltf,
#[serde(rename = "bvh_to_workflow")]
BvhToWorkflow
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(InferenceModelType);
impl_mysql_enum_coders!(InferenceModelType);
/// NB: Legacy API for older code.
impl InferenceModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::RvcV2 => "rvc_v2",
Self::SadTalker => "sad_talker",
Self::SoVitsSvc => "so_vits_svc",
Self::Tacotron2 => "tacotron2",
Self::Vits => "vits",
Self::VallEX => "vall_e_x",
Self::RerenderAVideo => "rerender_a_video",
Self::StableDiffusion => "stable_diffusion",
Self::ImageGenApi => "image_gen_api",
Self::SeedVc => "seed_vc",
Self::MocapNet => "mocap_net",
Self::StyleTTS2 => "styletts2",
Self::ComfyUi => "comfy_ui",
Self::ConvertFbxToGltf => "convert_fbx_gltf",
Self::BvhToWorkflow => "bvh_to_workflow",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"rvc_v2" => Ok(Self::RvcV2),
"sad_talker" => Ok(Self::SadTalker),
"so_vits_svc" => Ok(Self::SoVitsSvc),
"seed_vc" => Ok(Self::SeedVc),
"tacotron2" => Ok(Self::Tacotron2),
"vits" => Ok(Self::Vits),
"vall_e_x" => Ok(Self::VallEX),
"rerender_a_video" => Ok(Self::RerenderAVideo),
"stable_diffusion" => Ok(Self::StableDiffusion),
"image_gen_api" => Ok(Self::ImageGenApi),
"mocap_net" => Ok(Self::MocapNet),
"styletts2" => Ok(Self::StyleTTS2),
"comfy_ui" => Ok(Self::ComfyUi),
"convert_fbx_gltf" => Ok(Self::ConvertFbxToGltf),
"bvh_to_workflow" => Ok(Self::BvhToWorkflow),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::ComfyUi,
Self::RvcV2,
Self::SadTalker,
Self::SoVitsSvc,
Self::SeedVc,
Self::Tacotron2,
Self::Vits,
Self::VallEX,
Self::RerenderAVideo,
Self::StableDiffusion,
Self::ImageGenApi,
Self::MocapNet,
Self::StyleTTS2,
Self::ConvertFbxToGltf,
Self::BvhToWorkflow,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(InferenceModelType::RvcV2, "rvc_v2");
assert_serialization(InferenceModelType::SadTalker, "sad_talker");
assert_serialization(InferenceModelType::SeedVc, "seed_vc");
assert_serialization(InferenceModelType::SoVitsSvc, "so_vits_svc");
assert_serialization(InferenceModelType::Tacotron2, "tacotron2");
assert_serialization(InferenceModelType::Vits, "vits");
assert_serialization(InferenceModelType::VallEX, "vall_e_x");
assert_serialization(InferenceModelType::RerenderAVideo, "rerender_a_video");
assert_serialization(InferenceModelType::StableDiffusion, "stable_diffusion");
assert_serialization(InferenceModelType::ImageGenApi, "image_gen_api");
assert_serialization(InferenceModelType::MocapNet, "mocap_net");
assert_serialization(InferenceModelType::ComfyUi, "comfy_ui");
assert_serialization(InferenceModelType::StyleTTS2, "styletts2");
assert_serialization(InferenceModelType::ConvertFbxToGltf, "convert_fbx_gltf");
assert_serialization(InferenceModelType::BvhToWorkflow, "bvh_to_workflow");
}
#[test]
fn to_str() {
assert_eq!(InferenceModelType::ComfyUi.to_str(), "comfy_ui");
assert_eq!(InferenceModelType::RvcV2.to_str(), "rvc_v2");
assert_eq!(InferenceModelType::SadTalker.to_str(), "sad_talker");
assert_eq!(InferenceModelType::SoVitsSvc.to_str(), "so_vits_svc");
assert_eq!(InferenceModelType::SoVitsSvc.to_str(), "so_vits_svc");
assert_eq!(InferenceModelType::Tacotron2.to_str(), "tacotron2");
assert_eq!(InferenceModelType::Vits.to_str(), "vits");
assert_eq!(InferenceModelType::VallEX.to_str(), "vall_e_x");
assert_eq!(InferenceModelType::RerenderAVideo.to_str(), "rerender_a_video");
assert_eq!(InferenceModelType::StableDiffusion.to_str(), "stable_diffusion");
assert_eq!(InferenceModelType::ImageGenApi.to_str(), "image_gen_api");
assert_eq!(InferenceModelType::MocapNet.to_str(), "mocap_net");
assert_eq!(InferenceModelType::StyleTTS2.to_str(), "styletts2");
assert_eq!(InferenceModelType::ConvertFbxToGltf.to_str(), "convert_fbx_gltf");
assert_eq!(InferenceModelType::BvhToWorkflow.to_str(), "bvh_to_workflow");
}
#[test]
fn from_str() {
assert_eq!(InferenceModelType::from_str("rvc_v2").unwrap(), InferenceModelType::RvcV2);
assert_eq!(InferenceModelType::from_str("sad_talker").unwrap(), InferenceModelType::SadTalker);
assert_eq!(InferenceModelType::from_str("seed_vc").unwrap(), InferenceModelType::SeedVc);
assert_eq!(InferenceModelType::from_str("so_vits_svc").unwrap(), InferenceModelType::SoVitsSvc);
assert_eq!(InferenceModelType::from_str("tacotron2").unwrap(), InferenceModelType::Tacotron2);
assert_eq!(InferenceModelType::from_str("vits").unwrap(), InferenceModelType::Vits);
assert_eq!(InferenceModelType::from_str("vall_e_x").unwrap(), InferenceModelType::VallEX);
assert_eq!(InferenceModelType::from_str("rerender_a_video").unwrap(), InferenceModelType::RerenderAVideo);
assert_eq!(InferenceModelType::from_str("stable_diffusion").unwrap(), InferenceModelType::StableDiffusion);
assert_eq!(InferenceModelType::from_str("image_gen_api").unwrap(), InferenceModelType::ImageGenApi);
assert_eq!(InferenceModelType::from_str("mocap_net").unwrap(), InferenceModelType::MocapNet);
assert_eq!(InferenceModelType::from_str("styletts2").unwrap(), InferenceModelType::StyleTTS2);
assert_eq!(InferenceModelType::from_str("comfy_ui").unwrap(), InferenceModelType::ComfyUi);
assert_eq!(InferenceModelType::from_str("convert_fbx_gltf").unwrap(), InferenceModelType::ConvertFbxToGltf);
assert_eq!(InferenceModelType::from_str("bvh_to_workflow").unwrap(), InferenceModelType::BvhToWorkflow);
}
#[test]
fn all_variants() {
// Static check
let mut variants = InferenceModelType::all_variants();
assert_eq!(variants.len(), 15);
assert_eq!(variants.pop_first(), Some(InferenceModelType::ComfyUi));
assert_eq!(variants.pop_first(), Some(InferenceModelType::RvcV2));
assert_eq!(variants.pop_first(), Some(InferenceModelType::SadTalker));
assert_eq!(variants.pop_first(), Some(InferenceModelType::SoVitsSvc));
assert_eq!(variants.pop_first(), Some(InferenceModelType::SeedVc));
assert_eq!(variants.pop_first(), Some(InferenceModelType::Tacotron2));
assert_eq!(variants.pop_first(), Some(InferenceModelType::Vits));
assert_eq!(variants.pop_first(), Some(InferenceModelType::VallEX));
assert_eq!(variants.pop_first(), Some(InferenceModelType::RerenderAVideo));
assert_eq!(variants.pop_first(), Some(InferenceModelType::StableDiffusion));
assert_eq!(variants.pop_first(), Some(InferenceModelType::ImageGenApi));
assert_eq!(variants.pop_first(), Some(InferenceModelType::MocapNet));
assert_eq!(variants.pop_first(), Some(InferenceModelType::StyleTTS2));
assert_eq!(variants.pop_first(), Some(InferenceModelType::ConvertFbxToGltf));
assert_eq!(variants.pop_first(), Some(InferenceModelType::BvhToWorkflow));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(InferenceModelType::all_variants().len(), InferenceModelType::iter().len());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(InferenceModelType::all_variants().len(), InferenceModelType::iter().len());
}
#[test]
fn round_trip() {
for variant in InferenceModelType::all_variants() {
assert_eq!(variant, InferenceModelType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, InferenceModelType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, InferenceModelType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in InferenceModelType::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);
}
}
}
}
@@ -1,125 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `generic_inference_jobs` table in `VARCHAR(32)` field `on_success_result_entity_type`.
///
/// Our "generic inference" pipeline supports a wide variety of output types.
/// Each "result type" is identified by the following enum variants.
///
/// These types are present in the HTTP API and database columns as serialized here.
///
/// YOU CAN ADD NEW VALUES, BUT DO NOT CHANGE EXISTING VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum InferenceResultType {
/// Result is stored in the "media_files" table.
/// (The upstream model could have produced any type of media - image, video, audio. That is irrelevant.)
#[serde(rename = "media_file")]
MediaFile,
#[serde(rename = "text_to_speech")]
TextToSpeech,
#[serde(rename = "voice_conversion")]
VoiceConversion,
#[serde(rename = "zs_voice_embedding")]
ZeroShotVoiceEmbedding,
#[serde(rename = "upload_model")]
UploadModel
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(InferenceResultType);
impl_mysql_enum_coders!(InferenceResultType);
/// NB: Legacy API for older code.
impl InferenceResultType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::TextToSpeech => "text_to_speech",
Self::VoiceConversion => "voice_conversion",
Self::ZeroShotVoiceEmbedding => "zs_voice_embedding",
Self::UploadModel => "upload_model",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"text_to_speech" => Ok(Self::TextToSpeech),
"voice_conversion" => Ok(Self::VoiceConversion),
"zs_voice_embedding" => Ok(Self::ZeroShotVoiceEmbedding),
"upload_model" => Ok(Self::UploadModel),
_ => Err(format!("invalid value: {:?}", value)),
}
}
pub fn all_variants() -> BTreeSet<InferenceResultType> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
InferenceResultType::MediaFile,
InferenceResultType::TextToSpeech,
InferenceResultType::VoiceConversion,
InferenceResultType::ZeroShotVoiceEmbedding,
InferenceResultType::UploadModel,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_inference_jobs::inference_result_type::InferenceResultType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(InferenceResultType::MediaFile, "media_file");
assert_serialization(InferenceResultType::TextToSpeech, "text_to_speech");
assert_serialization(InferenceResultType::VoiceConversion, "voice_conversion");
assert_serialization(InferenceResultType::ZeroShotVoiceEmbedding, "zs_voice_embedding");
assert_serialization(InferenceResultType::UploadModel, "upload_model");
}
#[test]
fn to_str() {
assert_eq!(InferenceResultType::MediaFile.to_str(), "media_file");
assert_eq!(InferenceResultType::TextToSpeech.to_str(), "text_to_speech");
assert_eq!(InferenceResultType::VoiceConversion.to_str(), "voice_conversion");
assert_eq!(InferenceResultType::ZeroShotVoiceEmbedding.to_str(), "zs_voice_embedding");
assert_eq!(InferenceResultType::UploadModel.to_str(), "upload_model");
}
#[test]
fn from_str() {
assert_eq!(InferenceResultType::from_str("media_file").unwrap(), InferenceResultType::MediaFile);
assert_eq!(InferenceResultType::from_str("text_to_speech").unwrap(), InferenceResultType::TextToSpeech);
assert_eq!(InferenceResultType::from_str("voice_conversion").unwrap(), InferenceResultType::VoiceConversion);
assert_eq!(InferenceResultType::from_str("zs_voice_embedding").unwrap(), InferenceResultType::ZeroShotVoiceEmbedding);
assert_eq!(InferenceResultType::from_str("upload_model").unwrap(), InferenceResultType::UploadModel);
}
#[test]
fn all_variants() {
// Static check
let mut variants = InferenceResultType::all_variants();
assert_eq!(variants.len(), 5);
assert_eq!(variants.pop_first(), Some(InferenceResultType::MediaFile));
assert_eq!(variants.pop_first(), Some(InferenceResultType::TextToSpeech));
assert_eq!(variants.pop_first(), Some(InferenceResultType::VoiceConversion));
assert_eq!(variants.pop_first(), Some(InferenceResultType::ZeroShotVoiceEmbedding));
assert_eq!(variants.pop_first(), Some(InferenceResultType::UploadModel));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(InferenceResultType::all_variants().len(), InferenceResultType::iter().len());
}
}
@@ -1,9 +0,0 @@
pub mod frontend_failure_category;
pub mod inference_category;
pub mod inference_input_source_token_type;
pub mod inference_job_external_third_party;
pub mod inference_job_product_category;
// Moved to enums_db crate:
// pub mod inference_job_type;
pub mod inference_model_type;
pub mod inference_result_type;
@@ -1,259 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `generic_synthetic_ids` table in `VARCHAR(32)` field `id_category`.
///
/// This lets us create synthetic increment IDs on a per-user, per-category basis.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum IdCategory {
/// media_files table
#[serde(rename = "media_file")]
MediaFile,
/// Results from lipsync animations (which may live in the media_files table)
#[serde(rename = "lipsync_animation")]
LipsyncAnimationResult,
/// Results from face fusion
#[serde(rename = "face_fusion")]
FaceFusionResult,
/// Results from video filters
#[serde(rename = "video_filter")]
VideoFilterResult,
/// Results from Live Portrait
#[serde(rename = "live_portrait")]
LivePortraitResult,
/// Studio Renders
#[serde(rename = "studio_render")]
StudioRender,
/// Results from mocap
#[serde(rename = "mocap")]
MocapResult,
/// Results from workflows
#[serde(rename = "workflow")]
WorkflowResult,
/// Results from tacotron2
/// Applies for RVC and SVC
#[serde(rename = "tts_result")]
TtsResult,
/// Results from voice conversion (which may live in the media_files table)
/// Applies for RVC and SVC
#[serde(rename = "voice_conversion")]
VoiceConversionResult,
/// Results from the zero shot tts (which may live in the media_files table)
#[serde(rename = "zs_tts_result")]
ZeroShotTtsResult,
/// Zs dataset which lives in the zs_voice_datasets table
#[serde(rename = "zs_dataset")]
ZeroShotVoiceDataset,
/// Zs voice which lives in the zs_voices table
#[serde(rename = "zs_voice")]
ZeroShotVoiceEmbedding,
#[serde(rename = "model_weights")]
ModelWeights,
/// Files that are uploaded with no general product area they belong to. (Eg. local dev testing)
#[serde(rename = "file_upload")]
FileUpload,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(IdCategory);
impl_mysql_enum_coders!(IdCategory);
/// NB: Legacy API for older code.
impl IdCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::LipsyncAnimationResult => "lipsync_animation",
Self::FaceFusionResult => "face_fusion",
Self::TtsResult => "tts_result",
Self::VoiceConversionResult => "voice_conversion",
Self::ZeroShotVoiceDataset => "zs_dataset",
Self::ZeroShotVoiceEmbedding => "zs_voice",
Self::ZeroShotTtsResult => "zs_tts_result",
Self::VideoFilterResult => "video_filter",
Self::LivePortraitResult => "live_portrait",
Self::StudioRender => "studio_render",
Self::ModelWeights => "model_weights",
Self::FileUpload => "file_upload",
Self::MocapResult => "mocap",
Self::WorkflowResult => "workflow",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"lipsync_animation" => Ok(Self::LipsyncAnimationResult),
"face_fusion" => Ok(Self::FaceFusionResult),
"tts_result" => Ok(Self::TtsResult),
"voice_conversion" => Ok(Self::VoiceConversionResult),
"zs_dataset" => Ok(Self::ZeroShotVoiceDataset),
"zs_voice" => Ok(Self::ZeroShotVoiceEmbedding),
"zs_tts_result" => Ok(Self::ZeroShotTtsResult),
"video_filter" => Ok(Self::VideoFilterResult),
"live_portrait" => Ok(Self::LivePortraitResult),
"studio_render" => Ok(Self::StudioRender),
"model_weights" => Ok(Self::ModelWeights),
"file_upload" => Ok(Self::FileUpload),
"mocap" => Ok(Self::MocapResult),
"workflow" => Ok(Self::WorkflowResult),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::MediaFile,
Self::LipsyncAnimationResult,
Self::FaceFusionResult,
Self::VideoFilterResult,
Self::LivePortraitResult,
Self::StudioRender,
Self::TtsResult,
Self::VoiceConversionResult,
Self::ZeroShotTtsResult,
Self::ZeroShotVoiceDataset,
Self::ZeroShotVoiceEmbedding,
Self::ModelWeights,
Self::FileUpload,
Self::MocapResult,
Self::WorkflowResult,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::generic_synthetic_ids::id_category::IdCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(IdCategory::MediaFile, "media_file");
assert_serialization(IdCategory::LipsyncAnimationResult, "lipsync_animation");
assert_serialization(IdCategory::FaceFusionResult, "face_fusion");
assert_serialization(IdCategory::VideoFilterResult, "video_filter");
assert_serialization(IdCategory::LivePortraitResult, "live_portrait");
assert_serialization(IdCategory::StudioRender, "studio_render");
assert_serialization(IdCategory::TtsResult, "tts_result");
assert_serialization(IdCategory::VoiceConversionResult, "voice_conversion");
assert_serialization(IdCategory::ZeroShotVoiceDataset, "zs_dataset");
assert_serialization(IdCategory::ZeroShotVoiceEmbedding, "zs_voice");
assert_serialization(IdCategory::ZeroShotTtsResult, "zs_tts_result");
assert_serialization(IdCategory::ModelWeights, "model_weights");
assert_serialization(IdCategory::FileUpload, "file_upload");
assert_serialization(IdCategory::MocapResult, "mocap");
assert_serialization(IdCategory::WorkflowResult, "workflow");
}
#[test]
fn to_str() {
assert_eq!(IdCategory::MediaFile.to_str(), "media_file");
assert_eq!(IdCategory::LipsyncAnimationResult.to_str(), "lipsync_animation");
assert_eq!(IdCategory::FaceFusionResult.to_str(), "face_fusion");
assert_eq!(IdCategory::VideoFilterResult.to_str(), "video_filter");
assert_eq!(IdCategory::LivePortraitResult.to_str(), "live_portrait");
assert_eq!(IdCategory::StudioRender.to_str(), "studio_render");
assert_eq!(IdCategory::TtsResult.to_str(), "tts_result");
assert_eq!(IdCategory::VoiceConversionResult.to_str(), "voice_conversion");
assert_eq!(IdCategory::ZeroShotVoiceDataset.to_str(), "zs_dataset");
assert_eq!(IdCategory::ZeroShotVoiceEmbedding.to_str(), "zs_voice");
assert_eq!(IdCategory::ZeroShotTtsResult.to_str(), "zs_tts_result");
assert_eq!(IdCategory::ModelWeights.to_str(), "model_weights");
assert_eq!(IdCategory::FileUpload.to_str(), "file_upload");
assert_eq!(IdCategory::MocapResult.to_str(), "mocap");
assert_eq!(IdCategory::WorkflowResult.to_str(), "workflow");
}
#[test]
fn from_str() {
assert_eq!(IdCategory::from_str("media_file").unwrap(), IdCategory::MediaFile);
assert_eq!(IdCategory::from_str("lipsync_animation").unwrap(), IdCategory::LipsyncAnimationResult);
assert_eq!(IdCategory::from_str("face_fusion").unwrap(), IdCategory::FaceFusionResult);
assert_eq!(IdCategory::from_str("video_filter").unwrap(), IdCategory::VideoFilterResult);
assert_eq!(IdCategory::from_str("live_portrait").unwrap(), IdCategory::LivePortraitResult);
assert_eq!(IdCategory::from_str("studio_render").unwrap(), IdCategory::StudioRender);
assert_eq!(IdCategory::from_str("tts_result").unwrap(), IdCategory::TtsResult);
assert_eq!(IdCategory::from_str("voice_conversion").unwrap(), IdCategory::VoiceConversionResult);
assert_eq!(IdCategory::from_str("zs_dataset").unwrap(), IdCategory::ZeroShotVoiceDataset);
assert_eq!(IdCategory::from_str("zs_voice").unwrap(), IdCategory::ZeroShotVoiceEmbedding);
assert_eq!(IdCategory::from_str("zs_tts_result").unwrap(), IdCategory::ZeroShotTtsResult);
assert_eq!(IdCategory::from_str("model_weights").unwrap(), IdCategory::ModelWeights);
assert_eq!(IdCategory::from_str("file_upload").unwrap(), IdCategory::FileUpload);
assert_eq!(IdCategory::from_str("mocap").unwrap(), IdCategory::MocapResult);
assert_eq!(IdCategory::from_str("workflow").unwrap(), IdCategory::WorkflowResult);
}
#[test]
fn all_variants() {
// Static check
let mut variants = IdCategory::all_variants();
assert_eq!(variants.len(), 15);
assert_eq!(variants.pop_first(), Some(IdCategory::MediaFile));
assert_eq!(variants.pop_first(), Some(IdCategory::LipsyncAnimationResult));
assert_eq!(variants.pop_first(), Some(IdCategory::FaceFusionResult));
assert_eq!(variants.pop_first(), Some(IdCategory::VideoFilterResult));
assert_eq!(variants.pop_first(), Some(IdCategory::LivePortraitResult));
assert_eq!(variants.pop_first(), Some(IdCategory::StudioRender));
assert_eq!(variants.pop_first(), Some(IdCategory::MocapResult));
assert_eq!(variants.pop_first(), Some(IdCategory::WorkflowResult));
assert_eq!(variants.pop_first(), Some(IdCategory::TtsResult));
assert_eq!(variants.pop_first(), Some(IdCategory::VoiceConversionResult));
assert_eq!(variants.pop_first(), Some(IdCategory::ZeroShotTtsResult));
assert_eq!(variants.pop_first(), Some(IdCategory::ZeroShotVoiceDataset));
assert_eq!(variants.pop_first(), Some(IdCategory::ZeroShotVoiceEmbedding));
assert_eq!(variants.pop_first(), Some(IdCategory::ModelWeights));
assert_eq!(variants.pop_first(), Some(IdCategory::FileUpload));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(IdCategory::all_variants().len(), IdCategory::iter().len());
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(IdCategory::all_variants().len(), IdCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in IdCategory::all_variants() {
assert_eq!(variant, IdCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, IdCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, IdCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1 +0,0 @@
pub mod id_category;
@@ -1,245 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileAnimationType {
/// Body: No body animation or rig.
/// Face: Rig supporting ARKit face blendshapes, or ARKit data
ArKit,
/// Body: Animation or rig for MikuMikuDance (MMD).
/// Face: No face animation.
MikuMikuDance,
/// Body: Animation or rig for MikuMikuDance (MMD).
/// Face: Animation blendshapes for ARKit.
MikuMikuDanceArKit,
/// Body: Animation or rig for Mixamo
/// Face: No face animation.
Mixamo,
/// Body: Animation or rig for Mixamo
/// Face: Animation blendshapes for ARKit.
MixamoArKit,
/// Body: Animation or rig by MocapNet
/// Face: No face animation.
MocapNet,
/// Body: Animation or rig for MocapNet
/// Face: Animation blendshapes for ARKit.
MocapNetArKit,
/// Body: Animation or rig by Move.ai
/// Face: No face animation.
MoveAi,
/// Body: Animation or rig for Move.ai
/// Face: Animation blendshapes for ARKit.
MoveAiArKit,
/// Body: Animation or rig for Rigify.
/// Face: No face animation.
Rigify,
/// Body: Animation or rig for Rigify.
/// Face: Animation blendshapes for ARKit.
RigifyArKit,
/// Body: Animation or rig for Rokoko.
/// Face: No face animation.
Rokoko,
/// Body: Animation or rig for Rokoko.
/// Face: Animation blendshapes for ARKit.
RokokoArKit,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileAnimationType);
impl_mysql_enum_coders!(MediaFileAnimationType);
impl_mysql_from_row!(MediaFileAnimationType);
/// NB: Legacy API for older code.
impl MediaFileAnimationType {
pub fn to_str(&self) -> &'static str {
match self {
Self::ArKit => "ar_kit",
Self::MikuMikuDance => "miku_miku_dance",
Self::MikuMikuDanceArKit => "miku_miku_dance_ar_kit",
Self::Mixamo => "mixamo",
Self::MixamoArKit => "mixamo_ar_kit",
Self::MocapNet => "mocap_net",
Self::MocapNetArKit => "mocap_net_ar_kit",
Self::MoveAi => "move_ai",
Self::MoveAiArKit => "move_ai_ar_kit",
Self::Rigify => "rigify",
Self::RigifyArKit => "rigify_ar_kit",
Self::Rokoko => "rokoko",
Self::RokokoArKit => "rokoko_ar_kit",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"ar_kit" => Ok(Self::ArKit),
"miku_miku_dance" => Ok(Self::MikuMikuDance),
"miku_miku_dance_ar_kit" => Ok(Self::MikuMikuDanceArKit),
"mixamo" => Ok(Self::Mixamo),
"mixamo_ar_kit" => Ok(Self::MixamoArKit),
"mocap_net" => Ok(Self::MocapNet),
"mocap_net_ar_kit" => Ok(Self::MocapNetArKit),
"move_ai" => Ok(Self::MoveAi),
"move_ai_ar_kit" => Ok(Self::MoveAiArKit),
"rigify" => Ok(Self::Rigify),
"rigify_ar_kit" => Ok(Self::RigifyArKit),
"rokoko" => Ok(Self::Rokoko),
"rokoko_ar_kit" => Ok(Self::RokokoArKit),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::ArKit,
Self::MikuMikuDance,
Self::MikuMikuDanceArKit,
Self::Mixamo,
Self::MixamoArKit,
Self::MocapNet,
Self::MocapNetArKit,
Self::MoveAi,
Self::MoveAiArKit,
Self::Rigify,
Self::RigifyArKit,
Self::Rokoko,
Self::RokokoArKit,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_animation_type::MediaFileAnimationType;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileAnimationType::ArKit, "ar_kit");
assert_serialization(MediaFileAnimationType::MikuMikuDance, "miku_miku_dance");
assert_serialization(MediaFileAnimationType::MikuMikuDanceArKit, "miku_miku_dance_ar_kit");
assert_serialization(MediaFileAnimationType::Mixamo, "mixamo");
assert_serialization(MediaFileAnimationType::MixamoArKit, "mixamo_ar_kit");
assert_serialization(MediaFileAnimationType::MocapNet, "mocap_net");
assert_serialization(MediaFileAnimationType::MocapNetArKit, "mocap_net_ar_kit");
assert_serialization(MediaFileAnimationType::MoveAi, "move_ai");
assert_serialization(MediaFileAnimationType::MoveAiArKit, "move_ai_ar_kit");
assert_serialization(MediaFileAnimationType::Rigify, "rigify");
assert_serialization(MediaFileAnimationType::RigifyArKit, "rigify_ar_kit");
assert_serialization(MediaFileAnimationType::Rokoko, "rokoko");
assert_serialization(MediaFileAnimationType::RokokoArKit, "rokoko_ar_kit");
}
#[test]
fn test_to_str() {
assert_eq!(MediaFileAnimationType::ArKit.to_str(), "ar_kit");
assert_eq!(MediaFileAnimationType::MikuMikuDance.to_str(), "miku_miku_dance");
assert_eq!(MediaFileAnimationType::MikuMikuDanceArKit.to_str(), "miku_miku_dance_ar_kit");
assert_eq!(MediaFileAnimationType::Mixamo.to_str(), "mixamo");
assert_eq!(MediaFileAnimationType::MixamoArKit.to_str(), "mixamo_ar_kit");
assert_eq!(MediaFileAnimationType::MocapNet.to_str(), "mocap_net");
assert_eq!(MediaFileAnimationType::MocapNetArKit.to_str(), "mocap_net_ar_kit");
assert_eq!(MediaFileAnimationType::MoveAi.to_str(), "move_ai");
assert_eq!(MediaFileAnimationType::MoveAiArKit.to_str(), "move_ai_ar_kit");
assert_eq!(MediaFileAnimationType::Rigify.to_str(), "rigify");
assert_eq!(MediaFileAnimationType::RigifyArKit.to_str(), "rigify_ar_kit");
assert_eq!(MediaFileAnimationType::Rokoko.to_str(), "rokoko");
assert_eq!(MediaFileAnimationType::RokokoArKit.to_str(), "rokoko_ar_kit");
}
#[test]
fn test_from_str() {
assert_eq!(MediaFileAnimationType::from_str("ar_kit").unwrap(), MediaFileAnimationType::ArKit);
assert_eq!(MediaFileAnimationType::from_str("miku_miku_dance").unwrap(), MediaFileAnimationType::MikuMikuDance);
assert_eq!(MediaFileAnimationType::from_str("miku_miku_dance_ar_kit").unwrap(), MediaFileAnimationType::MikuMikuDanceArKit);
assert_eq!(MediaFileAnimationType::from_str("mixamo").unwrap(), MediaFileAnimationType::Mixamo);
assert_eq!(MediaFileAnimationType::from_str("mixamo_ar_kit").unwrap(), MediaFileAnimationType::MixamoArKit);
assert_eq!(MediaFileAnimationType::from_str("mocap_net").unwrap(), MediaFileAnimationType::MocapNet);
assert_eq!(MediaFileAnimationType::from_str("mocap_net_ar_kit").unwrap(), MediaFileAnimationType::MocapNetArKit);
assert_eq!(MediaFileAnimationType::from_str("move_ai").unwrap(), MediaFileAnimationType::MoveAi);
assert_eq!(MediaFileAnimationType::from_str("move_ai_ar_kit").unwrap(), MediaFileAnimationType::MoveAiArKit);
assert_eq!(MediaFileAnimationType::from_str("rigify").unwrap(), MediaFileAnimationType::Rigify);
assert_eq!(MediaFileAnimationType::from_str("rigify_ar_kit").unwrap(), MediaFileAnimationType::RigifyArKit);
assert_eq!(MediaFileAnimationType::from_str("rokoko").unwrap(), MediaFileAnimationType::Rokoko);
assert_eq!(MediaFileAnimationType::from_str("rokoko_ar_kit").unwrap(), MediaFileAnimationType::RokokoArKit);
assert!(MediaFileAnimationType::from_str("foo").is_err());
}
#[test]
fn all_variants() {
let mut variants = MediaFileAnimationType::all_variants();
assert_eq!(variants.len(), 13);
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::ArKit));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MikuMikuDance));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MikuMikuDanceArKit));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::Mixamo));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MixamoArKit));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MocapNet));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MocapNetArKit));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MoveAi));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MoveAiArKit));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::Rigify));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::RigifyArKit));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::Rokoko));
assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::RokokoArKit));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileAnimationType::all_variants().len(), MediaFileAnimationType::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileAnimationType::all_variants() {
assert_eq!(variant, MediaFileAnimationType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileAnimationType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileAnimationType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in MediaFileAnimationType::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);
}
}
}
}
@@ -1,159 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileClass {
/// Unknown (default value)
/// This will be present until we migrate all old files.
Unknown,
/// Audio files: wav, mp3, etc.
Audio,
/// Image files: png, jpeg, etc.
Image,
/// Video files: mp4, etc.
Video,
/// 3D engine data: glb, gltf, etc.
Dimensional,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileClass);
impl_mysql_enum_coders!(MediaFileClass);
impl_mysql_from_row!(MediaFileClass);
/// NB: Legacy API for older code.
impl MediaFileClass {
pub const fn to_str(&self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::Audio => "audio",
Self::Image => "image",
Self::Video => "video",
Self::Dimensional => "dimensional",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"unknown" => Ok(Self::Unknown),
"audio" => Ok(Self::Audio),
"image" => Ok(Self::Image),
"video" => Ok(Self::Video),
"dimensional" => Ok(Self::Dimensional),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Unknown,
Self::Audio,
Self::Image,
Self::Video,
Self::Dimensional,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_class::MediaFileClass;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileClass::Unknown, "unknown");
assert_serialization(MediaFileClass::Audio, "audio");
assert_serialization(MediaFileClass::Image, "image");
assert_serialization(MediaFileClass::Video, "video");
assert_serialization(MediaFileClass::Dimensional, "dimensional");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(MediaFileClass::Unknown.to_str(), "unknown");
assert_eq!(MediaFileClass::Audio.to_str(), "audio");
assert_eq!(MediaFileClass::Image.to_str(), "image");
assert_eq!(MediaFileClass::Video.to_str(), "video");
assert_eq!(MediaFileClass::Dimensional.to_str(), "dimensional");
}
#[test]
fn from_str() {
assert_eq!(MediaFileClass::from_str("unknown").unwrap(), MediaFileClass::Unknown);
assert_eq!(MediaFileClass::from_str("audio").unwrap(), MediaFileClass::Audio);
assert_eq!(MediaFileClass::from_str("image").unwrap(), MediaFileClass::Image);
assert_eq!(MediaFileClass::from_str("video").unwrap(), MediaFileClass::Video);
assert_eq!(MediaFileClass::from_str("dimensional").unwrap(), MediaFileClass::Dimensional);
assert!(MediaFileClass::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = MediaFileClass::all_variants();
assert_eq!(variants.len(), 5);
assert_eq!(variants.pop_first(), Some(MediaFileClass::Unknown));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Audio));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Image));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Video));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Dimensional));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileClass::all_variants().len(), MediaFileClass::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileClass::all_variants() {
assert_eq!(variant, MediaFileClass::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileClass::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileClass::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in MediaFileClass::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);
}
}
}
}
@@ -1,219 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileEngineCategory {
/// A 3D scene full of objects, characters, animations, etc.
Scene,
/// A 3D character model.
Character,
/// A 3D non-humanoid model, e.g. dragon, wolf, slime creature, etc.
/// These may or may not have a skeleton that can be animated with IK/FK.
Creature,
/// A 3D skeletal animation sequence that can be applied to a character,
/// creature, etc. Typically, these use Mixamo or Move.ai.
Animation,
/// A facial blend shape / shape key animation. Currently, these
/// are 100% ARKit.
Expression,
/// A large 3D object that can be used as a backdrop for a scene,
/// a.k.a. "set", "stage", etc. These are typically self-contained
/// locations, buildings, or environments.
Location,
/// All items used to add atmosphere to a scene, e.g. furniture, plants,
/// vehicles, radios, and certain props. (We'll reserve the word props for
/// "hand props" later.)
SetDressing,
/// A 3D object that doesn't fit with the other types.
Object,
/// A 3D skybox.
Skybox,
/// An Image Plane (texture that spawns as an object)
ImagePlane,
/// A Video Plane (texture that spawns as an object)
VideoPlane,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileEngineCategory);
impl_mysql_enum_coders!(MediaFileEngineCategory);
impl_mysql_from_row!(MediaFileEngineCategory);
/// NB: Legacy API for older code.
impl MediaFileEngineCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::Scene => "scene",
Self::Character => "character",
Self::Creature => "creature",
Self::Animation => "animation",
Self::Expression => "expression",
Self::Location => "location",
Self::SetDressing => "set_dressing",
Self::Object => "object",
Self::Skybox => "skybox",
Self::ImagePlane => "image_plane",
Self::VideoPlane => "video_plane",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"scene" => Ok(Self::Scene),
"character" => Ok(Self::Character),
"creature" => Ok(Self::Creature),
"animation" => Ok(Self::Animation),
"expression" => Ok(Self::Expression),
"location" => Ok(Self::Location),
"set_dressing" => Ok(Self::SetDressing),
"object" => Ok(Self::Object),
"skybox" => Ok(Self::Skybox),
"image_plane" => Ok(Self::ImagePlane),
"video_plane" => Ok(Self::VideoPlane),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Scene,
Self::Character,
Self::Creature,
Self::Animation,
Self::Expression,
Self::Location,
Self::SetDressing,
Self::Object,
Self::Skybox,
Self::ImagePlane,
Self::VideoPlane,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileEngineCategory::Scene, "scene");
assert_serialization(MediaFileEngineCategory::Character, "character");
assert_serialization(MediaFileEngineCategory::Creature, "creature");
assert_serialization(MediaFileEngineCategory::Animation, "animation");
assert_serialization(MediaFileEngineCategory::Expression, "expression");
assert_serialization(MediaFileEngineCategory::Location, "location");
assert_serialization(MediaFileEngineCategory::SetDressing, "set_dressing");
assert_serialization(MediaFileEngineCategory::Object, "object");
assert_serialization(MediaFileEngineCategory::Skybox, "skybox");
assert_serialization(MediaFileEngineCategory::ImagePlane, "image_plane");
assert_serialization(MediaFileEngineCategory::VideoPlane, "video_plane");
}
#[test]
fn test_to_str() {
assert_eq!(MediaFileEngineCategory::Scene.to_str(), "scene");
assert_eq!(MediaFileEngineCategory::Character.to_str(), "character");
assert_eq!(MediaFileEngineCategory::Creature.to_str(), "creature");
assert_eq!(MediaFileEngineCategory::Animation.to_str(), "animation");
assert_eq!(MediaFileEngineCategory::Expression.to_str(), "expression");
assert_eq!(MediaFileEngineCategory::Location.to_str(), "location");
assert_eq!(MediaFileEngineCategory::SetDressing.to_str(), "set_dressing");
assert_eq!(MediaFileEngineCategory::Object.to_str(), "object");
assert_eq!(MediaFileEngineCategory::Skybox.to_str(), "skybox");
assert_eq!(MediaFileEngineCategory::ImagePlane.to_str(), "image_plane");
assert_eq!(MediaFileEngineCategory::VideoPlane.to_str(), "video_plane");
}
#[test]
fn test_from_str() {
assert_eq!(MediaFileEngineCategory::from_str("scene").unwrap(), MediaFileEngineCategory::Scene);
assert_eq!(MediaFileEngineCategory::from_str("character").unwrap(), MediaFileEngineCategory::Character);
assert_eq!(MediaFileEngineCategory::from_str("creature").unwrap(), MediaFileEngineCategory::Creature);
assert_eq!(MediaFileEngineCategory::from_str("animation").unwrap(), MediaFileEngineCategory::Animation);
assert_eq!(MediaFileEngineCategory::from_str("expression").unwrap(), MediaFileEngineCategory::Expression);
assert_eq!(MediaFileEngineCategory::from_str("location").unwrap(), MediaFileEngineCategory::Location);
assert_eq!(MediaFileEngineCategory::from_str("set_dressing").unwrap(), MediaFileEngineCategory::SetDressing);
assert_eq!(MediaFileEngineCategory::from_str("object").unwrap(), MediaFileEngineCategory::Object);
assert_eq!(MediaFileEngineCategory::from_str("skybox").unwrap(), MediaFileEngineCategory::Skybox);
assert_eq!(MediaFileEngineCategory::from_str("image_plane").unwrap(), MediaFileEngineCategory::ImagePlane);
assert_eq!(MediaFileEngineCategory::from_str("video_plane").unwrap(), MediaFileEngineCategory::VideoPlane);
assert!(MediaFileEngineCategory::from_str("foo").is_err());
}
#[test]
fn all_variants() {
let mut variants = MediaFileEngineCategory::all_variants();
assert_eq!(variants.len(), 11);
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Scene));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Character));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Creature));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Animation));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Expression));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Location));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::SetDressing));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Object));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::Skybox));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::ImagePlane));
assert_eq!(variants.pop_first(), Some(MediaFileEngineCategory::VideoPlane));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileEngineCategory::all_variants().len(), MediaFileEngineCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileEngineCategory::all_variants() {
assert_eq!(variant, MediaFileEngineCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileEngineCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileEngineCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in MediaFileEngineCategory::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);
}
}
}
}
@@ -1,165 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileOriginCategory {
/// ML model inference output - uploaded models or zero shot.
Inference,
/// Processed results - (we don't have these systems yet, but eg. trim, transcode, etc).
Processed,
/// User uploaded files (from their filesystem)
Upload,
/// User uploaded files recorded directly from their device (browser, mobile), typically using device APIs.
DeviceApi,
/// From Storyteller Studio Engine
#[deprecated(note="This db field should only denote file provenance, not the product.")]
#[serde(rename = "studio")]
StorytellerStudio,
/// From Storyteller Studio Engine
#[deprecated(note = "DO NOT USE. Originally deprecated in favor of `StorytellerStudio`.")]
StoryEngine,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileOriginCategory);
impl_mysql_enum_coders!(MediaFileOriginCategory);
impl_mysql_from_row!(MediaFileOriginCategory);
/// NB: Legacy API for older code.
impl MediaFileOriginCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::Inference => "inference",
Self::Processed => "processed",
Self::Upload => "upload",
Self::DeviceApi => "device_api",
Self::StorytellerStudio => "studio",
Self::StoryEngine => "story_engine",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"inference" => Ok(Self::Inference),
"processed" => Ok(Self::Processed),
"upload" => Ok(Self::Upload),
"device_api" => Ok(Self::DeviceApi),
"studio" => Ok(Self::StorytellerStudio),
"story_engine" => Ok(Self::StoryEngine),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Inference,
Self::Processed,
Self::Upload,
Self::DeviceApi,
Self::StorytellerStudio,
Self::StoryEngine,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileOriginCategory::Inference, "inference");
assert_serialization(MediaFileOriginCategory::Processed, "processed");
assert_serialization(MediaFileOriginCategory::Upload, "upload");
assert_serialization(MediaFileOriginCategory::DeviceApi, "device_api");
assert_serialization(MediaFileOriginCategory::StorytellerStudio, "studio");
assert_serialization(MediaFileOriginCategory::StoryEngine, "story_engine");
}
#[test]
fn test_to_str() {
assert_eq!(MediaFileOriginCategory::Inference.to_str(), "inference");
assert_eq!(MediaFileOriginCategory::Processed.to_str(), "processed");
assert_eq!(MediaFileOriginCategory::Upload.to_str(), "upload");
assert_eq!(MediaFileOriginCategory::DeviceApi.to_str(), "device_api");
assert_eq!(MediaFileOriginCategory::StorytellerStudio.to_str(), "studio");
assert_eq!(MediaFileOriginCategory::StoryEngine.to_str(), "story_engine");
}
#[test]
fn test_from_str() {
assert_eq!(MediaFileOriginCategory::from_str("inference").unwrap(), MediaFileOriginCategory::Inference);
assert_eq!(MediaFileOriginCategory::from_str("processed").unwrap(), MediaFileOriginCategory::Processed);
assert_eq!(MediaFileOriginCategory::from_str("upload").unwrap(), MediaFileOriginCategory::Upload);
assert_eq!(MediaFileOriginCategory::from_str("device_api").unwrap(), MediaFileOriginCategory::DeviceApi);
assert_eq!(MediaFileOriginCategory::from_str("studio").unwrap(), MediaFileOriginCategory::StorytellerStudio);
assert_eq!(MediaFileOriginCategory::from_str("story_engine").unwrap(), MediaFileOriginCategory::StoryEngine);
assert!(MediaFileOriginCategory::from_str("foo").is_err());
}
#[test]
fn all_variants() {
let mut variants = MediaFileOriginCategory::all_variants();
assert_eq!(variants.len(), 6);
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::Inference));
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::Processed));
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::Upload));
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::DeviceApi));
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::StorytellerStudio));
assert_eq!(variants.pop_first(), Some(MediaFileOriginCategory::StoryEngine));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileOriginCategory::all_variants().len(), MediaFileOriginCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileOriginCategory::all_variants() {
assert_eq!(variant, MediaFileOriginCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileOriginCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileOriginCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in MediaFileOriginCategory::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);
}
}
}
}
@@ -1,280 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in `VARCHAR(16)` field `origin_product_category`.
///
/// This value indicates what product originally created the media file. (Not the ML model or
/// user upload process.) This will let us scope media files to the product that generated them
/// and filter them out of unrelated products if necessary (eg. a user probably doesn't want
/// "Voice Designer" dataset samples in a video generation flow.)
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
pub enum MediaFileOriginProductCategory {
/// Unknown which product is attached to the file (generated the file, the file was
/// uploaded on behalf of, etc.)
#[serde(rename = "unknown")]
Unknown,
/// Media files created by (or uploaded for) the Face Animator product.
/// The underlying model could be SadTalker, Wav2Lip, or some future model
#[serde(rename = "face_animator")]
FaceAnimator,
/// Media files created by Face Fusion (lipsync)
#[serde(rename = "face_fusion")]
FaceFusion,
/// Media files created by Face Mirror (currently powered by Live Portrait)
#[serde(rename = "face_mirror")]
FaceMirror,
/// Video style transfer
#[serde(rename = "vst")]
VideoStyleTransfer,
/// Image Studio
#[serde(rename = "image_studio")]
ImageStudio,
/// Storyteller Studio
#[serde(rename = "studio")]
StorytellerStudio,
/// Text to speech (Tacotron2, not voice designer / VallE-X)
#[serde(rename = "tts")]
TextToSpeech,
/// Voice conversion (either RVC or SVC)
#[serde(rename = "voice_conversion")]
VoiceConversion,
/// Media files created by (or uploaded for) the Zero Shot voice product.
#[serde(rename = "zs_voice")]
ZeroShotVoice,
// Mocap
#[serde(rename = "mocap")]
Mocap,
#[serde(rename = "image_gen")]
ImageGeneration,
#[serde(rename = "video_gen")]
VideoGeneration,
#[serde(rename = "world_gen")]
WorldGeneration,
// Media files for video filters
#[deprecated(note = "This isn't relevant product surface area anymore")]
#[serde(rename = "video_filter")]
VideoFilter,
// Workflow
#[deprecated(note = "Use studio and VST instead.")]
#[serde(rename = "workflow")]
Workflow,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileOriginProductCategory);
impl_mysql_enum_coders!(MediaFileOriginProductCategory);
impl_mysql_from_row!(MediaFileOriginProductCategory);
/// NB: Legacy API for older code.
impl MediaFileOriginProductCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::FaceAnimator => "face_animator",
Self::FaceFusion => "face_fusion",
Self::FaceMirror => "face_mirror",
Self::VideoStyleTransfer => "vst",
Self::ImageStudio => "image_studio",
Self::StorytellerStudio => "studio",
Self::TextToSpeech => "tts",
Self::VoiceConversion => "voice_conversion",
Self::ZeroShotVoice => "zs_voice",
Self::Mocap => "mocap",
Self::ImageGeneration => "image_gen",
Self::VideoGeneration => "video_gen",
Self::WorldGeneration => "world_gen",
Self::VideoFilter => "video_filter",
Self::Workflow => "workflow",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"unknown" => Ok(Self::Unknown),
"face_animator" => Ok(Self::FaceAnimator),
"face_fusion" => Ok(Self::FaceFusion),
"face_mirror" => Ok(Self::FaceMirror),
"vst" => Ok(Self::VideoStyleTransfer),
"image_studio" => Ok(Self::ImageStudio),
"studio" => Ok(Self::StorytellerStudio),
"tts" => Ok(Self::TextToSpeech),
"voice_conversion" => Ok(Self::VoiceConversion),
"zs_voice" => Ok(Self::ZeroShotVoice),
"mocap" => Ok(Self::Mocap),
"image_gen" => Ok(Self::ImageGeneration),
"video_gen" => Ok(Self::VideoGeneration),
"world_gen" => Ok(Self::WorldGeneration),
"video_filter" => Ok(Self::VideoFilter),
"workflow" => Ok(Self::Workflow),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Unknown,
Self::FaceAnimator,
Self::FaceFusion,
Self::FaceMirror,
Self::VideoStyleTransfer,
Self::ImageStudio,
Self::StorytellerStudio,
Self::TextToSpeech,
Self::VoiceConversion,
Self::ZeroShotVoice,
Self::Mocap,
Self::ImageGeneration,
Self::VideoGeneration,
Self::WorldGeneration,
Self::VideoFilter,
Self::Workflow,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileOriginProductCategory::Unknown, "unknown");
assert_serialization(MediaFileOriginProductCategory::FaceAnimator, "face_animator");
assert_serialization(MediaFileOriginProductCategory::FaceFusion, "face_fusion");
assert_serialization(MediaFileOriginProductCategory::FaceMirror, "face_mirror");
assert_serialization(MediaFileOriginProductCategory::VideoStyleTransfer, "vst");
assert_serialization(MediaFileOriginProductCategory::ImageStudio, "image_studio");
assert_serialization(MediaFileOriginProductCategory::StorytellerStudio, "studio");
assert_serialization(MediaFileOriginProductCategory::TextToSpeech, "tts");
assert_serialization(MediaFileOriginProductCategory::VoiceConversion, "voice_conversion");
assert_serialization(MediaFileOriginProductCategory::ZeroShotVoice, "zs_voice");
assert_serialization(MediaFileOriginProductCategory::Mocap, "mocap");
assert_serialization(MediaFileOriginProductCategory::ImageGeneration, "image_gen");
assert_serialization(MediaFileOriginProductCategory::VideoGeneration, "video_gen");
assert_serialization(MediaFileOriginProductCategory::WorldGeneration, "world_gen");
assert_serialization(MediaFileOriginProductCategory::VideoFilter, "video_filter");
assert_serialization(MediaFileOriginProductCategory::Workflow, "workflow");
}
#[test]
fn to_str() {
assert_eq!(MediaFileOriginProductCategory::Unknown.to_str(), "unknown");
assert_eq!(MediaFileOriginProductCategory::FaceAnimator.to_str(), "face_animator");
assert_eq!(MediaFileOriginProductCategory::FaceFusion.to_str(), "face_fusion");
assert_eq!(MediaFileOriginProductCategory::FaceMirror.to_str(), "face_mirror");
assert_eq!(MediaFileOriginProductCategory::VideoStyleTransfer.to_str(), "vst");
assert_eq!(MediaFileOriginProductCategory::ImageStudio.to_str(), "image_studio");
assert_eq!(MediaFileOriginProductCategory::StorytellerStudio.to_str(), "studio");
assert_eq!(MediaFileOriginProductCategory::TextToSpeech.to_str(), "tts");
assert_eq!(MediaFileOriginProductCategory::VoiceConversion.to_str(), "voice_conversion");
assert_eq!(MediaFileOriginProductCategory::ZeroShotVoice.to_str(), "zs_voice");
assert_eq!(MediaFileOriginProductCategory::Mocap.to_str(), "mocap");
assert_eq!(MediaFileOriginProductCategory::ImageGeneration.to_str(), "image_gen");
assert_eq!(MediaFileOriginProductCategory::VideoGeneration.to_str(), "video_gen");
assert_eq!(MediaFileOriginProductCategory::WorldGeneration.to_str(), "world_gen");
assert_eq!(MediaFileOriginProductCategory::VideoFilter.to_str(), "video_filter");
assert_eq!(MediaFileOriginProductCategory::Workflow.to_str(), "workflow");
}
#[test]
fn from_str() {
assert_eq!(MediaFileOriginProductCategory::from_str("unknown").unwrap(), MediaFileOriginProductCategory::Unknown);
assert_eq!(MediaFileOriginProductCategory::from_str("face_animator").unwrap(), MediaFileOriginProductCategory::FaceAnimator);
assert_eq!(MediaFileOriginProductCategory::from_str("face_fusion").unwrap(), MediaFileOriginProductCategory::FaceFusion);
assert_eq!(MediaFileOriginProductCategory::from_str("face_mirror").unwrap(), MediaFileOriginProductCategory::FaceMirror);
assert_eq!(MediaFileOriginProductCategory::from_str("vst").unwrap(), MediaFileOriginProductCategory::VideoStyleTransfer);
assert_eq!(MediaFileOriginProductCategory::from_str("image_studio").unwrap(), MediaFileOriginProductCategory::ImageStudio);
assert_eq!(MediaFileOriginProductCategory::from_str("studio").unwrap(), MediaFileOriginProductCategory::StorytellerStudio);
assert_eq!(MediaFileOriginProductCategory::from_str("tts").unwrap(), MediaFileOriginProductCategory::TextToSpeech);
assert_eq!(MediaFileOriginProductCategory::from_str("voice_conversion").unwrap(), MediaFileOriginProductCategory::VoiceConversion);
assert_eq!(MediaFileOriginProductCategory::from_str("zs_voice").unwrap(), MediaFileOriginProductCategory::ZeroShotVoice);
assert_eq!(MediaFileOriginProductCategory::from_str("mocap").unwrap(), MediaFileOriginProductCategory::Mocap);
assert_eq!(MediaFileOriginProductCategory::from_str("image_gen").unwrap(),MediaFileOriginProductCategory::ImageGeneration);
assert_eq!(MediaFileOriginProductCategory::from_str("video_gen").unwrap(), MediaFileOriginProductCategory::VideoGeneration);
assert_eq!(MediaFileOriginProductCategory::from_str("world_gen").unwrap(), MediaFileOriginProductCategory::WorldGeneration);
assert_eq!(MediaFileOriginProductCategory::from_str("video_filter").unwrap(), MediaFileOriginProductCategory::VideoFilter);
assert_eq!(MediaFileOriginProductCategory::from_str("workflow").unwrap(), MediaFileOriginProductCategory::Workflow);
}
#[test]
fn all_variants() {
let mut variants = MediaFileOriginProductCategory::all_variants();
assert_eq!(variants.len(), 16);
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Unknown));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::FaceAnimator));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::FaceFusion));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::FaceMirror));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VideoStyleTransfer));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::ImageStudio));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::StorytellerStudio));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::TextToSpeech));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VoiceConversion));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::ZeroShotVoice));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Mocap));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::ImageGeneration));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VideoGeneration));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::WorldGeneration));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VideoFilter));
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Workflow));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileOriginProductCategory::all_variants().len(), MediaFileOriginProductCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileOriginProductCategory::all_variants() {
assert_eq!(variant, MediaFileOriginProductCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileOriginProductCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileOriginProductCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in MediaFileOriginProductCategory::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);
}
}
}
}
@@ -1,224 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
#[deprecated(note = "This was primarily for Bevy")]
pub enum MediaFileSubtype {
/// NB: MediaFileSubtype is deprecated.
/// This should signal that the field / enum is fully deprecated.
Deprecated,
// TODO(bt,2024-04-22): Deprecated (migrate)
/// Animation file from Mixamo
/// Primarily used for FBX and GLB.
Mixamo,
// TODO(bt,2024-04-22): Deprecated (migrate)
/// Animation file from MocapNet
/// Primarily used for BVH.
MocapNet,
// TODO(bt,2024-04-22): Deprecated
/// Generic animation case
/// Used for BVH files, but can also pertain to animation-only files of other types.
AnimationOnly,
// TODO(bt,2024-04-22): Deprecated
/// Generic 3D scene file.
/// Can pertain to glTF, glB, FBX, etc.
SceneImport,
// TODO(bt,2024-04-22): Deprecated
/// Native Storyteller scene format.
/// Typically stored in a `.scn.ron` file.
StorytellerScene,
/// A 3D scene full of objects, characters, animations, etc.
Scene,
/// A 3D character model.
Character,
/// A 3D animation.
Animation,
/// A 3D object that doesn't fit with the other types.
Object,
/// A 3D skybox.
Skybox,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileSubtype);
impl_mysql_enum_coders!(MediaFileSubtype);
impl_mysql_from_row!(MediaFileSubtype);
/// NB: Legacy API for older code.
impl MediaFileSubtype {
pub fn to_str(&self) -> &'static str {
match self {
Self::Deprecated => "deprecated",
Self::Mixamo => "mixamo",
Self::MocapNet => "mocap_net",
Self::AnimationOnly => "animation_only",
Self::SceneImport => "scene_import",
Self::StorytellerScene => "storyteller_scene",
Self::Scene => "scene",
Self::Character => "character",
Self::Animation => "animation",
Self::Object => "object",
Self::Skybox => "skybox",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"deprecated" => Ok(Self::Deprecated),
"mixamo" => Ok(Self::Mixamo),
"mocap_net" => Ok(Self::MocapNet),
"animation_only" => Ok(Self::AnimationOnly),
"scene_import" => Ok(Self::SceneImport),
"storyteller_scene" => Ok(Self::StorytellerScene),
"scene" => Ok(Self::Scene),
"character" => Ok(Self::Character),
"animation" => Ok(Self::Animation),
"object" => Ok(Self::Object),
"skybox" => Ok(Self::Skybox),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Deprecated,
Self::Mixamo,
Self::MocapNet,
Self::AnimationOnly,
Self::SceneImport,
Self::StorytellerScene,
Self::Scene,
Self::Character,
Self::Animation,
Self::Object,
Self::Skybox,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_subtype::MediaFileSubtype;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileSubtype::Deprecated, "deprecated");
assert_serialization(MediaFileSubtype::Mixamo, "mixamo");
assert_serialization(MediaFileSubtype::MocapNet, "mocap_net");
assert_serialization(MediaFileSubtype::AnimationOnly, "animation_only");
assert_serialization(MediaFileSubtype::SceneImport, "scene_import");
assert_serialization(MediaFileSubtype::StorytellerScene, "storyteller_scene");
assert_serialization(MediaFileSubtype::Scene, "scene");
assert_serialization(MediaFileSubtype::Character, "character");
assert_serialization(MediaFileSubtype::Animation, "animation");
assert_serialization(MediaFileSubtype::Object, "object");
assert_serialization(MediaFileSubtype::Skybox, "skybox");
}
#[test]
fn test_to_str() {
assert_eq!(MediaFileSubtype::Deprecated.to_str(), "deprecated");
assert_eq!(MediaFileSubtype::Mixamo.to_str(), "mixamo");
assert_eq!(MediaFileSubtype::MocapNet.to_str(), "mocap_net");
assert_eq!(MediaFileSubtype::AnimationOnly.to_str(), "animation_only");
assert_eq!(MediaFileSubtype::SceneImport.to_str(), "scene_import");
assert_eq!(MediaFileSubtype::StorytellerScene.to_str(), "storyteller_scene");
assert_eq!(MediaFileSubtype::Scene.to_str(), "scene");
assert_eq!(MediaFileSubtype::Character.to_str(), "character");
assert_eq!(MediaFileSubtype::Animation.to_str(), "animation");
assert_eq!(MediaFileSubtype::Object.to_str(), "object");
assert_eq!(MediaFileSubtype::Skybox.to_str(), "skybox");
}
#[test]
fn test_from_str() {
assert_eq!(MediaFileSubtype::from_str("deprecated").unwrap(), MediaFileSubtype::Deprecated);
assert_eq!(MediaFileSubtype::from_str("mixamo").unwrap(), MediaFileSubtype::Mixamo);
assert_eq!(MediaFileSubtype::from_str("mocap_net").unwrap(), MediaFileSubtype::MocapNet);
assert_eq!(MediaFileSubtype::from_str("animation_only").unwrap(), MediaFileSubtype::AnimationOnly);
assert_eq!(MediaFileSubtype::from_str("scene_import").unwrap(), MediaFileSubtype::SceneImport);
assert_eq!(MediaFileSubtype::from_str("storyteller_scene").unwrap(), MediaFileSubtype::StorytellerScene);
assert_eq!(MediaFileSubtype::from_str("scene").unwrap(), MediaFileSubtype::Scene);
assert_eq!(MediaFileSubtype::from_str("character").unwrap(), MediaFileSubtype::Character);
assert_eq!(MediaFileSubtype::from_str("animation").unwrap(), MediaFileSubtype::Animation);
assert_eq!(MediaFileSubtype::from_str("object").unwrap(), MediaFileSubtype::Object);
assert_eq!(MediaFileSubtype::from_str("skybox").unwrap(), MediaFileSubtype::Skybox);
assert!(MediaFileSubtype::from_str("foo").is_err());
}
#[test]
fn all_variants() {
let mut variants = MediaFileSubtype::all_variants();
assert_eq!(variants.len(), 11);
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Deprecated));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Mixamo));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::MocapNet));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::AnimationOnly));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::SceneImport));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::StorytellerScene));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Scene));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Character));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Animation));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Object));
assert_eq!(variants.pop_first(), Some(MediaFileSubtype::Skybox));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileSubtype::all_variants().len(), MediaFileSubtype::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileSubtype::all_variants() {
assert_eq!(variant, MediaFileSubtype::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileSubtype::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileSubtype::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in MediaFileSubtype::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);
}
}
}
}
@@ -1,376 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileType {
// TODO(bt): Deprecate and split into audio mime types; use media_class to represent broadly
/// Audio files: wav, mp3, etc.
#[deprecated]
Audio,
// TODO(bt): Deprecate and split into image mime types; use media_class to represent broadly
/// Image files: png, jpeg, etc.
#[deprecated]
Image,
// TODO(bt): Deprecate and split into video mime types; use media_class to represent broadly
/// Video files: mp4, etc.
#[deprecated]
Video,
/// BVH files (for Bevy)
/// NB: This is the new type to migrate to.
Bvh,
/// FBX files (for Bevy)
Fbx,
/// glTF binary files (for Bevy)
Glb,
/// glTF files (for Bevy)
Gltf,
/// Spz Gaussian splats
/// This is a standard file format for Gaussian Splats, used by World Labs.
/// The mime type is application/gzip.
Spz,
/// Bevy's scene files (in RON; Rusty Object Notation)
/// This will be replaced with another format in future versions of Bevy
SceneRon,
/// Alternate scene files.
SceneJson,
/// "Polygon Model Data", character data for MikuMikuDance
/// See: https://mikumikudance.fandom.com/wiki/MMD:Polygon_Model_Data
Pmd,
/// "Vocaloid Motion Data", animation data for MikuMikuDance
/// See: https://mikumikudance.fandom.com/wiki/VMD_file_format
Vmd,
/// "Polygon Model eXtend", character data from MikuMikuDance
/// NB: this is often associated with external files for textures, which
/// we'll store in the same bucket path.
/// See: https://mikumikudance.fandom.com/wiki/MMD:Polygon_Model_eXtend
Pmx,
/// CSV format. (We use these for ArKit)
Csv,
/// Jpeg images
Jpg,
/// Png images
Png,
/// Gif images
Gif,
/// Mp4 videos
Mp4,
/// Wav audio
Wav,
/// Mp3 audio
Mp3,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaFileType);
impl_mysql_enum_coders!(MediaFileType);
impl_mysql_from_row!(MediaFileType);
/// NB: Legacy API for older code.
impl MediaFileType {
/// Jpeg and Png are the most widely supported static image formats for AI inference.
/// Webp, Webm, and Gif aren't as widely supported, so we don't include them here.
pub fn is_jpg_or_png(&self) -> bool {
matches!(self, Self::Jpg | Self::Png)
}
/// A little bit less strict than `is_jpg_or_png`, this includes the legacy `Image` type.
pub fn is_jpg_or_png_or_legacy_image(&self) -> bool {
matches!(self, Self::Jpg | Self::Png | Self::Image)
}
/// Returns the `MediaFileType` if the mime type matches one of the known types.
/// This is not exhaustive.
/// (NB: In general we shouldn't keep much application logic in the `enums` crate,
/// but this is a good case for it.)
pub fn try_from_mime_type(mime_type: &str) -> Option<Self> {
match mime_type {
"image/jpeg" => Some(Self::Jpg),
"image/png" => Some(Self::Png),
"video/mp4" => Some(Self::Mp4),
"model/gltf-binary" => Some(Self::Glb),
//"audio/wav" => Some(Self::Wav), // NB: Not sure if this is correct
//"audio/mpeg" => Some(Self::Mp3), // NB: Not sure if this is correct
//"image/gif" => Some(Self::Gif), // NB: Not sure if this is correct
//"model/gltf+json" => Some(Self::Gltf), // NB: Not sure if this is correct
_ => None,
}
}
pub fn to_str(&self) -> &'static str {
match self {
Self::Audio => "audio",
Self::Image => "image",
Self::Video => "video",
Self::Bvh => "bvh",
Self::Fbx => "fbx",
Self::Glb => "glb",
Self::Gltf => "gltf",
Self::Spz => "spz",
Self::SceneRon => "scene_ron",
Self::SceneJson => "scene_json",
Self::Pmd => "pmd",
Self::Vmd => "vmd",
Self::Pmx => "pmx",
Self::Csv => "csv",
Self::Jpg => "jpg",
Self::Png => "png",
Self::Gif => "gif",
Self::Mp4 => "mp4",
Self::Wav => "wav",
Self::Mp3 => "mp3",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"audio" => Ok(Self::Audio),
"image" => Ok(Self::Image),
"video" => Ok(Self::Video),
"bvh" => Ok(Self::Bvh),
"fbx" => Ok(Self::Fbx),
"glb" => Ok(Self::Glb),
"gltf" => Ok(Self::Gltf),
"spz" => Ok(Self::Spz),
"scene_ron" => Ok(Self::SceneRon),
"scene_json" => Ok(Self::SceneJson),
"pmd" => Ok(Self::Pmd),
"vmd" => Ok(Self::Vmd),
"pmx" => Ok(Self::Pmx),
"csv" => Ok(Self::Csv),
"jpg" => Ok(Self::Jpg),
"png" => Ok(Self::Png),
"gif" => Ok(Self::Gif),
"mp4" => Ok(Self::Mp4),
"wav" => Ok(Self::Wav),
"mp3" => Ok(Self::Mp3),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Audio,
Self::Image,
Self::Video,
Self::Bvh,
Self::Fbx,
Self::Glb,
Self::Gltf,
Self::Spz,
Self::SceneRon,
Self::SceneJson,
Self::Pmd,
Self::Vmd,
Self::Pmx,
Self::Csv,
Self::Jpg,
Self::Png,
Self::Gif,
Self::Mp4,
Self::Wav,
Self::Mp3,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_type::MediaFileType;
use crate::test_helpers::assert_serialization;
mod utility {
use super::*;
#[test]
fn test_jpg_or_png() {
// True
assert!(MediaFileType::Jpg.is_jpg_or_png());
assert!(MediaFileType::Png.is_jpg_or_png());
// Assert these image types are false
assert!(!MediaFileType::Gif.is_jpg_or_png());
assert!(!MediaFileType::Image.is_jpg_or_png());
// Everything else is false
for variant in MediaFileType::all_variants() {
if matches!(variant, MediaFileType::Jpg | MediaFileType::Png) {
continue;
}
assert!(!variant.is_jpg_or_png(), "Expected {:?} to not be jpg or png", variant);
}
}
}
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileType::Audio, "audio");
assert_serialization(MediaFileType::Image, "image");
assert_serialization(MediaFileType::Video, "video");
assert_serialization(MediaFileType::Bvh, "bvh");
assert_serialization(MediaFileType::Fbx, "fbx");
assert_serialization(MediaFileType::Glb, "glb");
assert_serialization(MediaFileType::Gltf, "gltf");
assert_serialization(MediaFileType::Spz, "spz");
assert_serialization(MediaFileType::SceneRon, "scene_ron");
assert_serialization(MediaFileType::SceneJson, "scene_json");
assert_serialization(MediaFileType::Pmd, "pmd");
assert_serialization(MediaFileType::Vmd, "vmd");
assert_serialization(MediaFileType::Pmx, "pmx");
assert_serialization(MediaFileType::Csv, "csv");
assert_serialization(MediaFileType::Jpg, "jpg");
assert_serialization(MediaFileType::Png, "png");
assert_serialization(MediaFileType::Gif, "gif");
assert_serialization(MediaFileType::Mp4, "mp4");
assert_serialization(MediaFileType::Wav, "wav");
assert_serialization(MediaFileType::Mp3, "mp3");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(MediaFileType::Audio.to_str(), "audio");
assert_eq!(MediaFileType::Image.to_str(), "image");
assert_eq!(MediaFileType::Video.to_str(), "video");
assert_eq!(MediaFileType::Bvh.to_str(), "bvh");
assert_eq!(MediaFileType::Fbx.to_str(), "fbx");
assert_eq!(MediaFileType::Glb.to_str(), "glb");
assert_eq!(MediaFileType::Gltf.to_str(), "gltf");
assert_eq!(MediaFileType::Spz.to_str(), "spz");
assert_eq!(MediaFileType::SceneRon.to_str(), "scene_ron");
assert_eq!(MediaFileType::SceneJson.to_str(), "scene_json");
assert_eq!(MediaFileType::Pmd.to_str(), "pmd");
assert_eq!(MediaFileType::Vmd.to_str(), "vmd");
assert_eq!(MediaFileType::Pmx.to_str(), "pmx");
assert_eq!(MediaFileType::Csv.to_str(), "csv");
assert_eq!(MediaFileType::Jpg.to_str(), "jpg");
assert_eq!(MediaFileType::Png.to_str(), "png");
assert_eq!(MediaFileType::Gif.to_str(), "gif");
assert_eq!(MediaFileType::Mp4.to_str(), "mp4");
assert_eq!(MediaFileType::Wav.to_str(), "wav");
assert_eq!(MediaFileType::Mp3.to_str(), "mp3");
}
#[test]
fn from_str() {
assert_eq!(MediaFileType::from_str("audio").unwrap(), MediaFileType::Audio);
assert_eq!(MediaFileType::from_str("image").unwrap(), MediaFileType::Image);
assert_eq!(MediaFileType::from_str("video").unwrap(), MediaFileType::Video);
assert_eq!(MediaFileType::from_str("bvh").unwrap(), MediaFileType::Bvh);
assert_eq!(MediaFileType::from_str("fbx").unwrap(), MediaFileType::Fbx);
assert_eq!(MediaFileType::from_str("glb").unwrap(), MediaFileType::Glb);
assert_eq!(MediaFileType::from_str("gltf").unwrap(), MediaFileType::Gltf);
assert_eq!(MediaFileType::from_str("spz").unwrap(), MediaFileType::Spz);
assert_eq!(MediaFileType::from_str("scene_ron").unwrap(), MediaFileType::SceneRon);
assert_eq!(MediaFileType::from_str("scene_json").unwrap(), MediaFileType::SceneJson);
assert_eq!(MediaFileType::from_str("pmd").unwrap(), MediaFileType::Pmd);
assert_eq!(MediaFileType::from_str("vmd").unwrap(), MediaFileType::Vmd);
assert_eq!(MediaFileType::from_str("pmx").unwrap(), MediaFileType::Pmx);
assert_eq!(MediaFileType::from_str("csv").unwrap(), MediaFileType::Csv);
assert_eq!(MediaFileType::from_str("jpg").unwrap(), MediaFileType::Jpg);
assert_eq!(MediaFileType::from_str("png").unwrap(), MediaFileType::Png);
assert_eq!(MediaFileType::from_str("gif").unwrap(), MediaFileType::Gif);
assert_eq!(MediaFileType::from_str("mp4").unwrap(), MediaFileType::Mp4);
assert_eq!(MediaFileType::from_str("wav").unwrap(), MediaFileType::Wav);
assert_eq!(MediaFileType::from_str("mp3").unwrap(), MediaFileType::Mp3);
assert!(MediaFileType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = MediaFileType::all_variants();
assert_eq!(variants.len(), 20);
assert_eq!(variants.pop_first(), Some(MediaFileType::Audio));
assert_eq!(variants.pop_first(), Some(MediaFileType::Image));
assert_eq!(variants.pop_first(), Some(MediaFileType::Video));
assert_eq!(variants.pop_first(), Some(MediaFileType::Bvh));
assert_eq!(variants.pop_first(), Some(MediaFileType::Fbx));
assert_eq!(variants.pop_first(), Some(MediaFileType::Glb));
assert_eq!(variants.pop_first(), Some(MediaFileType::Gltf));
assert_eq!(variants.pop_first(), Some(MediaFileType::Spz));
assert_eq!(variants.pop_first(), Some(MediaFileType::SceneRon));
assert_eq!(variants.pop_first(), Some(MediaFileType::SceneJson));
assert_eq!(variants.pop_first(), Some(MediaFileType::Pmd));
assert_eq!(variants.pop_first(), Some(MediaFileType::Vmd));
assert_eq!(variants.pop_first(), Some(MediaFileType::Pmx));
assert_eq!(variants.pop_first(), Some(MediaFileType::Csv));
assert_eq!(variants.pop_first(), Some(MediaFileType::Jpg));
assert_eq!(variants.pop_first(), Some(MediaFileType::Png));
assert_eq!(variants.pop_first(), Some(MediaFileType::Gif));
assert_eq!(variants.pop_first(), Some(MediaFileType::Mp4));
assert_eq!(variants.pop_first(), Some(MediaFileType::Wav));
assert_eq!(variants.pop_first(), Some(MediaFileType::Mp3));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileType::all_variants().len(), MediaFileType::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileType::all_variants() {
assert_eq!(variant, MediaFileType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in MediaFileType::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);
}
}
}
}
@@ -1,9 +0,0 @@
pub mod media_file_animation_type;
pub mod media_file_class;
pub mod media_file_engine_category;
pub mod media_file_origin_category;
// Moved to enums_db crate:
// pub mod media_file_origin_model_type;
pub mod media_file_origin_product_category;
pub mod media_file_subtype;
pub mod media_file_type;
@@ -1,77 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `media_uploads` table in a `VARCHAR` field `media_source`.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
pub enum MediaUploadSource {
/// Eg. browser javascript APIs to access the microphone, webcam, etc.
#[serde(rename = "device_api")]
DeviceApi,
/// Uploaded files from the filesystem
#[serde(rename = "file")]
File,
/// Unknown sources
#[serde(rename = "unknown")]
Unknown,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaUploadSource);
impl_mysql_enum_coders!(MediaUploadSource);
/// NB: Legacy API for older code.
impl MediaUploadSource {
pub fn to_str(&self) -> &'static str {
match self {
Self::DeviceApi => "device_api",
Self::File => "file",
Self::Unknown => "unknown",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"device_api" => Ok(Self::DeviceApi),
"file" => Ok(Self::File),
"unknown" => Ok(Self::Unknown),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_uploads::media_upload_source::MediaUploadSource;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(MediaUploadSource::DeviceApi, "device_api");
assert_serialization(MediaUploadSource::File, "file");
assert_serialization(MediaUploadSource::Unknown, "unknown");
}
#[test]
fn test_to_str() {
assert_eq!(MediaUploadSource::DeviceApi.to_str(), "device_api");
assert_eq!(MediaUploadSource::File.to_str(), "file");
assert_eq!(MediaUploadSource::Unknown.to_str(), "unknown");
}
#[test]
fn test_from_str() {
assert_eq!(MediaUploadSource::from_str("device_api").unwrap(), MediaUploadSource::DeviceApi);
assert_eq!(MediaUploadSource::from_str("file").unwrap(), MediaUploadSource::File);
assert_eq!(MediaUploadSource::from_str("unknown").unwrap(), MediaUploadSource::Unknown);
assert!(MediaUploadSource::from_str("foo").is_err());
}
}
@@ -1,83 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `media_uploads` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum MediaUploadType {
/// Audio files: wav, mp3, etc.
Audio,
/// Image files: png, jpeg, etc.
Image,
/// Video files: mp4, etc.
Video,
// Binary files: safetensors ... weights, etc.
Binary,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(MediaUploadType);
impl_mysql_enum_coders!(MediaUploadType);
/// NB: Legacy API for older code.
impl MediaUploadType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Audio => "audio",
Self::Image => "image",
Self::Video => "video",
Self::Binary => "binary",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"audio" => Ok(Self::Audio),
"image" => Ok(Self::Image),
"video" => Ok(Self::Video),
"binary" => Ok(Self::Binary),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_uploads::media_upload_type::MediaUploadType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(MediaUploadType::Audio, "audio");
assert_serialization(MediaUploadType::Image, "image");
assert_serialization(MediaUploadType::Video, "video");
assert_serialization(MediaUploadType::Binary, "binary");
}
#[test]
fn test_to_str() {
assert_eq!(MediaUploadType::Audio.to_str(), "audio");
assert_eq!(MediaUploadType::Image.to_str(), "image");
assert_eq!(MediaUploadType::Video.to_str(), "video");
assert_eq!(MediaUploadType::Binary.to_str(), "binary");
}
#[test]
fn test_from_str() {
assert_eq!(MediaUploadType::from_str("audio").unwrap(), MediaUploadType::Audio);
assert_eq!(MediaUploadType::from_str("image").unwrap(), MediaUploadType::Image);
assert_eq!(MediaUploadType::from_str("video").unwrap(), MediaUploadType::Video);
assert_eq!(MediaUploadType::from_str("binary").unwrap(), MediaUploadType::Binary);
assert!(MediaUploadType::from_str("foo").is_err());
}
}
@@ -1,2 +0,0 @@
pub mod media_upload_type;
pub mod media_upload_source;
@@ -1,33 +0,0 @@
// ===== MySql =====
pub mod audit_logs;
pub mod batch_generations;
pub mod beta_keys;
pub mod comments;
pub mod email_sender_jobs;
pub mod entity_stats;
pub mod featured_items;
pub mod generic_download_jobs;
pub mod generic_inference_jobs;
pub mod generic_synthetic_ids;
pub mod media_files;
pub mod media_uploads;
pub mod model_categories;
pub mod model_weights;
pub mod prompt_context_items;
pub mod prompts;
pub mod tag_uses;
pub mod trending_model_analytics;
pub mod tts_models;
pub mod usages;
pub mod user_bookmarks;
pub mod user_ratings;
pub mod users;
pub mod voice_conversion_models;
pub mod voice_conversion_results;
pub mod wallet_ledger_entries;
pub mod zs_voices;
// ===== Sqlite =====
pub mod tts_render_tasks;
pub mod web_rendition_targets;
pub mod web_scraping_targets;
@@ -1 +0,0 @@
pub mod model_type;
@@ -1,65 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `model_categories` table in an `ENUM` field.
/// (*WE WANT TO STOP USING ENUM FIELDS DUE TO MIGRATION ISSUES*)
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ModelType {
Tts,
W2l,
}
// TODO(bt, 2023-01-16): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(ModelType);
impl_mysql_enum_coders!(ModelType);
/// NB: Legacy API for older code.
impl ModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Tts => "tts",
Self::W2l => "w2l",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"tts" => Ok(Self::Tts),
"w2l" => Ok(Self::W2l),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::model_categories::model_type::ModelType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(ModelType::Tts, "tts");
assert_serialization(ModelType::W2l, "w2l");
}
#[test]
fn test_to_str() {
assert_eq!(ModelType::Tts.to_str(), "tts");
assert_eq!(ModelType::W2l.to_str(), "w2l");
}
#[test]
fn test_from_str() {
assert_eq!(ModelType::from_str("tts").unwrap(), ModelType::Tts);
assert_eq!(ModelType::from_str("w2l").unwrap(), ModelType::W2l);
assert!(ModelType::from_str("foo").is_err());
}
}
@@ -1,4 +0,0 @@
// Moved to enums_db crate:
// pub mod weights_types;
pub mod weights_category;
pub mod weights_file_type;
@@ -1,93 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
pub enum WeightsCategory {
#[serde(rename = "image_generation")]
ImageGeneration,
#[serde(rename = "text_to_speech")]
TextToSpeech,
#[serde(rename = "vocoder")]
Vocoder,
#[serde(rename = "voice_conversion")]
VoiceConversion,
#[serde(rename = "workflow_config")]
WorkflowConfig,
}
impl WeightsCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::ImageGeneration => "image_generation",
Self::TextToSpeech => "text_to_speech",
Self::Vocoder => "vocoder",
Self::VoiceConversion => "voice_conversion",
Self::WorkflowConfig => "workflow_config",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"image_generation" => Ok(Self::ImageGeneration),
"text_to_speech" => Ok(Self::TextToSpeech),
"vocoder" => Ok(Self::Vocoder),
"voice_conversion" => Ok(Self::VoiceConversion),
"workflow_config" => Ok(Self::WorkflowConfig),
_ => Err(format!("invalid value: {:?}", value)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
BTreeSet::from([
Self::ImageGeneration,
Self::TextToSpeech,
Self::Vocoder,
Self::VoiceConversion,
Self::WorkflowConfig,
])
}
}
impl_enum_display_and_debug_using_to_str!(WeightsCategory);
impl_mysql_enum_coders!(WeightsCategory);
impl_mysql_from_row!(WeightsCategory);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(WeightsCategory::ImageGeneration.to_str(), "image_generation");
assert_eq!(WeightsCategory::TextToSpeech.to_str(), "text_to_speech");
assert_eq!(WeightsCategory::Vocoder.to_str(), "vocoder");
assert_eq!(WeightsCategory::VoiceConversion.to_str(), "voice_conversion");
assert_eq!(WeightsCategory::WorkflowConfig.to_str(), "workflow_config");
}
#[test]
fn test_from_str() {
assert_eq!(WeightsCategory::from_str("image_generation").unwrap(), WeightsCategory::ImageGeneration);
assert_eq!(WeightsCategory::from_str("text_to_speech").unwrap(), WeightsCategory::TextToSpeech);
assert_eq!(WeightsCategory::from_str("vocoder").unwrap(), WeightsCategory::Vocoder);
assert_eq!(WeightsCategory::from_str("voice_conversion").unwrap(), WeightsCategory::VoiceConversion);
assert_eq!(WeightsCategory::from_str("workflow_config").unwrap(), WeightsCategory::WorkflowConfig);
assert!(WeightsCategory::from_str("invalid").is_err());
}
#[test]
fn test_all_variants() {
let variants = WeightsCategory::all_variants();
assert_eq!(variants.len(), 5);
assert!(variants.contains(&WeightsCategory::ImageGeneration));
assert!(variants.contains(&WeightsCategory::TextToSpeech));
assert!(variants.contains(&WeightsCategory::Vocoder));
assert!(variants.contains(&WeightsCategory::VoiceConversion));
assert!(variants.contains(&WeightsCategory::WorkflowConfig));
}
}
@@ -1,69 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
// TODO we will need to scan the checkpoints for malicious code. We can't just trust the file extension.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum WeightsFileType {
#[serde(rename = "checkpoint")]
Checkpoint,
#[serde(rename = "safetensors")]
SafeTensors,
}
impl WeightsFileType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Checkpoint => "checkpoint",
Self::SafeTensors => "safetensors",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"checkpoint" => Ok(Self::Checkpoint),
"safetensors" => Ok(Self::SafeTensors),
_ => Err(format!("invalid value: {:?}", value)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
BTreeSet::from([
Self::Checkpoint,
Self::SafeTensors,
])
}
}
impl_enum_display_and_debug_using_to_str!(WeightsFileType);
impl_mysql_enum_coders!(WeightsFileType);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(WeightsFileType::Checkpoint.to_str(), "checkpoint");
assert_eq!(WeightsFileType::SafeTensors.to_str(), "safetensors");
}
#[test]
fn test_from_str() {
assert_eq!(WeightsFileType::from_str("checkpoint").unwrap(), WeightsFileType::Checkpoint);
assert_eq!(WeightsFileType::from_str("safetensors").unwrap(), WeightsFileType::SafeTensors);
assert!(WeightsFileType::from_str("invalid").is_err());
}
#[test]
fn test_all_variants() {
let variants = WeightsFileType::all_variants();
assert_eq!(variants.len(), 2);
assert!(variants.contains(&WeightsFileType::Checkpoint));
assert!(variants.contains(&WeightsFileType::SafeTensors));
}
}
@@ -1 +0,0 @@
pub mod prompt_context_semantic_type;
@@ -1,213 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/*
Current DB values - this is in conflict with API serializations. Need to fix!
imgref
imgsrc
imgmask
vid_start_frame
vid_end_frame
vidref
*/
/// Used in the `prompt_context_items` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PromptContextSemanticType {
/// Image-to-Video starting frame
VidStartFrame,
/// Image-to-Video ending frame
VidEndFrame,
/// Reference image for video generation (e.g. Seedance "vidref" mode)
VidRef,
/// Source image, eg. for inpainting.
Imgsrc,
/// Image mask, eg. for inpainting.
Imgmask,
/// Standard image reference without a semantic type (e.g. Sora/ChatGPT 4o/gpt-image-1)
Imgref,
ImgrefCharacter,
ImgrefStyle,
ImgrefBg,
/// Audio reference (e.g. for audio-to-video generation)
Audioref,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(PromptContextSemanticType);
impl_mysql_enum_coders!(PromptContextSemanticType);
impl_mysql_from_row!(PromptContextSemanticType);
/// NB: Legacy API for older code.
impl PromptContextSemanticType {
pub fn to_str(&self) -> &'static str {
match self {
Self::VidStartFrame => "vid_start_frame",
Self::VidEndFrame => "vid_end_frame",
Self::VidRef => "vidref",
Self::Imgsrc => "imgsrc",
Self::Imgmask => "imgmask",
Self::Imgref => "imgref",
Self::ImgrefCharacter => "imgref_character",
Self::ImgrefStyle => "imgref_style",
Self::ImgrefBg => "imgref_bg",
Self::Audioref => "audioref",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"vid_start_frame" => Ok(Self::VidStartFrame),
"vid_end_frame" => Ok(Self::VidEndFrame),
"vidref" => Ok(Self::VidRef),
"imgsrc" => Ok(Self::Imgsrc),
"imgmask" => Ok(Self::Imgmask),
"imgref" => Ok(Self::Imgref),
"imgref_character" => Ok(Self::ImgrefCharacter),
"imgref_style" => Ok(Self::ImgrefStyle),
"imgref_bg" => Ok(Self::ImgrefBg),
"audioref" => Ok(Self::Audioref),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::VidStartFrame,
Self::VidEndFrame,
Self::VidRef,
Self::Imgsrc,
Self::Imgmask,
Self::Imgref,
Self::ImgrefCharacter,
Self::ImgrefStyle,
Self::ImgrefBg,
Self::Audioref,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(PromptContextSemanticType::VidStartFrame, "vid_start_frame");
assert_serialization(PromptContextSemanticType::VidEndFrame, "vid_end_frame");
assert_serialization(PromptContextSemanticType::VidRef, "vidref");
assert_serialization(PromptContextSemanticType::Imgsrc, "imgsrc");
assert_serialization(PromptContextSemanticType::Imgmask, "imgmask");
assert_serialization(PromptContextSemanticType::Imgref, "imgref");
assert_serialization(PromptContextSemanticType::ImgrefCharacter, "imgref_character");
assert_serialization(PromptContextSemanticType::ImgrefStyle, "imgref_style");
assert_serialization(PromptContextSemanticType::ImgrefBg, "imgref_bg");
assert_serialization(PromptContextSemanticType::Audioref, "audioref");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(PromptContextSemanticType::VidStartFrame.to_str(), "vid_start_frame");
assert_eq!(PromptContextSemanticType::VidEndFrame.to_str(), "vid_end_frame");
assert_eq!(PromptContextSemanticType::VidRef.to_str(), "vidref");
assert_eq!(PromptContextSemanticType::Imgsrc.to_str(), "imgsrc");
assert_eq!(PromptContextSemanticType::Imgmask.to_str(), "imgmask");
assert_eq!(PromptContextSemanticType::Imgref.to_str(), "imgref");
assert_eq!(PromptContextSemanticType::ImgrefCharacter.to_str(), "imgref_character");
assert_eq!(PromptContextSemanticType::ImgrefStyle.to_str(), "imgref_style");
assert_eq!(PromptContextSemanticType::ImgrefBg.to_str(), "imgref_bg");
assert_eq!(PromptContextSemanticType::Audioref.to_str(), "audioref");
}
#[test]
fn from_str() {
assert_eq!(PromptContextSemanticType::from_str("vid_start_frame").unwrap(), PromptContextSemanticType::VidStartFrame);
assert_eq!(PromptContextSemanticType::from_str("vid_end_frame").unwrap(), PromptContextSemanticType::VidEndFrame);
assert_eq!(PromptContextSemanticType::from_str("vidref").unwrap(), PromptContextSemanticType::VidRef);
assert_eq!(PromptContextSemanticType::from_str("imgsrc").unwrap(), PromptContextSemanticType::Imgsrc);
assert_eq!(PromptContextSemanticType::from_str("imgmask").unwrap(), PromptContextSemanticType::Imgmask);
assert_eq!(PromptContextSemanticType::from_str("imgref").unwrap(), PromptContextSemanticType::Imgref);
assert_eq!(PromptContextSemanticType::from_str("imgref_character").unwrap(), PromptContextSemanticType::ImgrefCharacter);
assert_eq!(PromptContextSemanticType::from_str("imgref_style").unwrap(), PromptContextSemanticType::ImgrefStyle);
assert_eq!(PromptContextSemanticType::from_str("imgref_bg").unwrap(), PromptContextSemanticType::ImgrefBg);
assert_eq!(PromptContextSemanticType::from_str("audioref").unwrap(), PromptContextSemanticType::Audioref);
assert!(PromptContextSemanticType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = PromptContextSemanticType::all_variants();
assert_eq!(variants.len(), 10);
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::VidStartFrame));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::VidEndFrame));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::VidRef));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::Imgsrc));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::Imgmask));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::Imgref));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::ImgrefCharacter));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::ImgrefStyle));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::ImgrefBg));
assert_eq!(variants.pop_first(), Some(PromptContextSemanticType::Audioref));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(PromptContextSemanticType::all_variants().len(), PromptContextSemanticType::iter().len());
}
#[test]
fn round_trip() {
for variant in PromptContextSemanticType::all_variants() {
assert_eq!(variant, PromptContextSemanticType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, PromptContextSemanticType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, PromptContextSemanticType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in PromptContextSemanticType::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);
}
}
}
}
@@ -1 +0,0 @@
pub mod prompt_type;
@@ -1,140 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `prompts` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PromptType {
/// Artcraft (App)
ArtcraftApp,
/// Stable diffusion
#[deprecated]
StableDiffusion,
/// Comfy UI
#[deprecated]
ComfyUi,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(PromptType);
impl_mysql_enum_coders!(PromptType);
impl_mysql_from_row!(PromptType);
/// NB: Legacy API for older code.
impl PromptType {
pub fn to_str(&self) -> &'static str {
match self {
Self::ArtcraftApp => "artcraft_app",
Self::StableDiffusion => "stable_diffusion",
Self::ComfyUi => "comfy_ui",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"artcraft_app" => Ok(Self::ArtcraftApp),
"stable_diffusion" => Ok(Self::StableDiffusion),
"comfy_ui" => Ok(Self::ComfyUi),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::ArtcraftApp,
Self::StableDiffusion,
Self::ComfyUi,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::prompts::prompt_type::PromptType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(PromptType::ArtcraftApp, "artcraft_app");
assert_serialization(PromptType::StableDiffusion, "stable_diffusion");
assert_serialization(PromptType::ComfyUi, "comfy_ui");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(PromptType::ArtcraftApp.to_str(), "artcraft_app");
assert_eq!(PromptType::StableDiffusion.to_str(), "stable_diffusion");
assert_eq!(PromptType::ComfyUi.to_str(), "comfy_ui");
}
#[test]
fn from_str() {
assert_eq!(PromptType::from_str("artcraft_app").unwrap(), PromptType::ArtcraftApp);
assert_eq!(PromptType::from_str("stable_diffusion").unwrap(), PromptType::StableDiffusion);
assert_eq!(PromptType::from_str("comfy_ui").unwrap(), PromptType::ComfyUi);
assert!(PromptType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = PromptType::all_variants();
assert_eq!(variants.len(), 3);
assert_eq!(variants.pop_first(), Some(PromptType::ArtcraftApp));
assert_eq!(variants.pop_first(), Some(PromptType::StableDiffusion));
assert_eq!(variants.pop_first(), Some(PromptType::ComfyUi));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(PromptType::all_variants().len(), PromptType::iter().len());
}
#[test]
fn round_trip() {
for variant in PromptType::all_variants() {
assert_eq!(variant, PromptType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, PromptType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, PromptType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in PromptType::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);
}
}
}
}
@@ -1 +0,0 @@
pub mod tag_use_entity_type;
@@ -1,127 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `tag_uses` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
pub enum TagUseEntityType {
/// Media files
#[serde(rename = "media_file")]
MediaFile,
/// Model weights
#[serde(rename = "model_weight")]
ModelWeight,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(TagUseEntityType);
impl_mysql_enum_coders!(TagUseEntityType);
/// NB: Legacy API for older code.
impl TagUseEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::MediaFile,
Self::ModelWeight,
])
}
}
#[cfg(test)]
mod tests {
use crate::test_helpers::assert_serialization;
use crate::by_table::tag_uses::tag_use_entity_type::TagUseEntityType;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(TagUseEntityType::MediaFile, "media_file");
assert_serialization(TagUseEntityType::ModelWeight, "model_weight");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(TagUseEntityType::MediaFile.to_str(), "media_file");
assert_eq!(TagUseEntityType::ModelWeight.to_str(), "model_weight");
}
#[test]
fn test_from_str() {
assert_eq!(TagUseEntityType::from_str("media_file").unwrap(), TagUseEntityType::MediaFile);
assert_eq!(TagUseEntityType::from_str("model_weight").unwrap(), TagUseEntityType::ModelWeight);
assert!(TagUseEntityType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = TagUseEntityType::all_variants();
assert_eq!(variants.len(), 2);
assert_eq!(variants.pop_first(), Some(TagUseEntityType::MediaFile));
assert_eq!(variants.pop_first(), Some(TagUseEntityType::ModelWeight));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(TagUseEntityType::all_variants().len(), TagUseEntityType::iter().len());
}
#[test]
fn round_trip() {
for variant in TagUseEntityType::all_variants() {
assert_eq!(variant, TagUseEntityType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, TagUseEntityType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, TagUseEntityType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in TagUseEntityType::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);
}
}
}
}
@@ -1,2 +0,0 @@
pub mod model_type;
pub mod window_name;
@@ -1,62 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `trending_model_analytics` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ModelType {
/// TTS models
Tts,
// NB: It's assumed we'll use this same system to track
// trending VC models too, so the next type would be "VC".
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(ModelType);
impl_mysql_enum_coders!(ModelType);
/// NB: Legacy API for older code.
impl ModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Tts => "tts",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"tts" => Ok(Self::Tts),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::trending_model_analytics::model_type::ModelType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(ModelType::Tts, "tts");
}
#[test]
fn test_to_str() {
assert_eq!(ModelType::Tts.to_str(), "tts");
}
#[test]
fn test_from_str() {
assert_eq!(ModelType::from_str("tts").unwrap(), ModelType::Tts);
assert!(ModelType::from_str("foo").is_err());
}
}
@@ -1,87 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `window_name` table in a `VARCHAR` field.
///
/// Contrary to most of this crate and unlike most "enum"-types
/// that are inflexible, new window names can be added/removed
/// without breaking too much.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum WindowName {
/// Analytics over the last three hours
#[serde(rename = "last_3_hours")]
Last3Hours,
/// Analytics over the last three hours
#[serde(rename = "last_3_days")]
Last3Days,
/// Analytics over all historical records
#[serde(rename = "all_time")]
AllTime,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(WindowName);
impl_mysql_enum_coders!(WindowName);
/// NB: Legacy API for older code.
impl WindowName {
pub fn to_str(&self) -> &'static str {
match self {
Self::Last3Hours => "last_3_hours",
Self::Last3Days => "last_3_days",
Self::AllTime => "all_time",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"last_3_hours" => Ok(Self::Last3Hours),
"last_3_days" => Ok(Self::Last3Days),
"all_time" => Ok(Self::AllTime),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::trending_model_analytics::window_name::WindowName;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(WindowName::Last3Hours, "last_3_hours");
assert_serialization(WindowName::Last3Days, "last_3_days");
assert_serialization(WindowName::AllTime, "all_time");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(WindowName::Last3Hours.to_str(), "last_3_hours");
assert_eq!(WindowName::Last3Days.to_str(), "last_3_days");
assert_eq!(WindowName::AllTime.to_str(), "all_time");
}
#[test]
fn test_from_str() {
assert_eq!(WindowName::from_str("last_3_hours").unwrap(), WindowName::Last3Hours);
assert_eq!(WindowName::from_str("last_3_days").unwrap(), WindowName::Last3Days);
assert_eq!(WindowName::from_str("all_time").unwrap(), WindowName::AllTime);
assert!(WindowName::from_str("foo").is_err());
}
}
}
@@ -1 +0,0 @@
pub mod tts_model_type;
@@ -1,67 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `tts_models` table in an `ENUM` field.
/// -- Furthermore -- not all enum values are represented !!
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
pub enum TtsModelType {
#[serde(rename = "tacotron2")]
Tacotron2,
#[serde(rename = "vits")]
Vits,
}
// TODO(bt, 2023-04-03): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(TtsModelType);
impl_mysql_enum_coders!(TtsModelType);
/// NB: Legacy API for older code.
impl TtsModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Tacotron2 => "tacotron2",
Self::Vits => "vits",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"tacotron2" => Ok(Self::Tacotron2),
"vits" => Ok(Self::Vits),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::tts_models::tts_model_type::TtsModelType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(TtsModelType::Tacotron2, "tacotron2");
assert_serialization(TtsModelType::Vits, "vits");
}
#[test]
fn test_to_str() {
assert_eq!(TtsModelType::Tacotron2.to_str(), "tacotron2");
assert_eq!(TtsModelType::Vits.to_str(), "vits");
}
#[test]
fn test_from_str() {
assert_eq!(TtsModelType::from_str("tacotron2").unwrap(), TtsModelType::Tacotron2);
assert_eq!(TtsModelType::from_str("vits").unwrap(), TtsModelType::Vits);
assert!(TtsModelType::from_str("foo").is_err());
}
}
@@ -1 +0,0 @@
pub mod tts_render_status;
@@ -1,105 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the SqLite `tts_render_tasks` table in a `TEXT` field named `tts_render_status`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum TtsRenderStatus {
#[serde(rename = "new")]
New,
// TODO: Added to fix a big. This whole enum should die.
#[serde(rename = "processing")]
Processing,
#[serde(rename = "skipped")]
Skipped,
#[serde(rename = "failed")]
Failed,
#[serde(rename = "permanently_failed")]
PermanentlyFailed,
#[serde(rename = "success")]
Success,
}
// TODO(bt, 2023-01-17): This desperately needs Sqlite integration tests!
impl_enum_display_and_debug_using_to_str!(TtsRenderStatus);
impl_sqlite_enum_coders!(TtsRenderStatus);
/// NB: Legacy API for older code.
impl TtsRenderStatus {
pub fn to_str(&self) -> &'static str {
match self {
Self::New => "new",
Self::Processing => "processing",
Self::Skipped => "skipped",
Self::Failed => "failed",
Self::PermanentlyFailed => "permanently_failed",
Self::Success => "success",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"new" => Ok(Self::New),
"processing" => Ok(Self::Processing),
"skipped" => Ok(Self::Skipped),
"failed" => Ok(Self::Failed),
"permanently_failed" => Ok(Self::PermanentlyFailed),
"success" => Ok(Self::Success),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::tts_render_tasks::tts_render_status::TtsRenderStatus;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(TtsRenderStatus::New, "new");
assert_serialization(TtsRenderStatus::Processing, "processing");
assert_serialization(TtsRenderStatus::Skipped, "skipped");
assert_serialization(TtsRenderStatus::Failed, "failed");
assert_serialization(TtsRenderStatus::PermanentlyFailed, "permanently_failed");
assert_serialization(TtsRenderStatus::Success, "success");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(TtsRenderStatus::New.to_str(), "new");
assert_eq!(TtsRenderStatus::Processing.to_str(), "processing");
assert_eq!(TtsRenderStatus::Skipped.to_str(), "skipped");
assert_eq!(TtsRenderStatus::Failed.to_str(), "failed");
assert_eq!(TtsRenderStatus::PermanentlyFailed.to_str(), "permanently_failed");
assert_eq!(TtsRenderStatus::Success.to_str(), "success");
}
#[test]
fn test_from_str() {
assert_eq!(TtsRenderStatus::from_str("new").unwrap(), TtsRenderStatus::New);
assert_eq!(TtsRenderStatus::from_str("processing").unwrap(), TtsRenderStatus::Processing);
assert_eq!(TtsRenderStatus::from_str("skipped").unwrap(), TtsRenderStatus::Skipped);
assert_eq!(TtsRenderStatus::from_str("failed").unwrap(), TtsRenderStatus::Failed);
assert_eq!(TtsRenderStatus::from_str("permanently_failed").unwrap(), TtsRenderStatus::PermanentlyFailed);
assert_eq!(TtsRenderStatus::from_str("success").unwrap(), TtsRenderStatus::Success);
assert!(TtsRenderStatus::from_str("foo").is_err());
}
}
}
@@ -1 +0,0 @@
pub mod usages_type;
@@ -1,128 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `usages` table in a `VARCHAR(16)` field. (Two fields!)
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum UsagesType {
/// Represents a foreign key link against a model_weights record
ModelWeight,
/// Represents a foreign key link against a media_files record
MediaFile,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(UsagesType);
impl_mysql_enum_coders!(UsagesType);
impl_mysql_from_row!(UsagesType);
/// NB: Legacy API for older code.
impl UsagesType {
pub fn to_str(&self) -> &'static str {
match self {
Self::ModelWeight => "model_weight",
Self::MediaFile => "media_file",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"model_weight" => Ok(Self::ModelWeight),
"media_file" => Ok(Self::MediaFile),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::ModelWeight,
Self::MediaFile,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::usages::usages_type::UsagesType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(UsagesType::ModelWeight, "model_weight");
assert_serialization(UsagesType::MediaFile, "media_file");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(UsagesType::ModelWeight.to_str(), "model_weight");
assert_eq!(UsagesType::MediaFile.to_str(), "media_file");
}
#[test]
fn from_str() {
assert_eq!(UsagesType::from_str("model_weight").unwrap(), UsagesType::ModelWeight);
assert_eq!(UsagesType::from_str("media_file").unwrap(), UsagesType::MediaFile);
assert!(UsagesType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = UsagesType::all_variants();
assert_eq!(variants.len(), 2);
assert_eq!(variants.pop_first(), Some(UsagesType::ModelWeight));
assert_eq!(variants.pop_first(), Some(UsagesType::MediaFile));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(UsagesType::all_variants().len(), UsagesType::iter().len());
}
#[test]
fn round_trip() {
for variant in UsagesType::all_variants() {
assert_eq!(variant, UsagesType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, UsagesType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, UsagesType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in UsagesType::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);
}
}
}
}
@@ -1 +0,0 @@
pub mod user_bookmark_entity_type;
@@ -1,139 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `user_bookmarks` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, ToSchema)]
pub enum UserBookmarkEntityType {
/// User
#[serde(rename = "user")]
User,
/// TTS model (architecture does not matter)
#[serde(rename = "tts_model")]
TtsModel,
/// TTS result (architecture does not matter)
#[serde(rename = "tts_result")]
TtsResult,
/// W2L template
#[serde(rename = "w2l_template")]
W2lTemplate,
/// W2L result
#[serde(rename = "w2l_result")]
W2lResult,
/// MediaFile
#[serde(rename = "media_file")]
MediaFile,
/// ModelWeight (the new way to store models)
#[serde(rename = "model_weight")]
ModelWeight,
/// VoiceConversionModel
#[serde(rename = "voice_conversion_model")]
VoiceConversionModel,
/// ZsVoice
#[serde(rename = "zs_voice")]
ZsVoice,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(UserBookmarkEntityType);
impl_mysql_enum_coders!(UserBookmarkEntityType);
impl_mysql_from_row!(UserBookmarkEntityType);
/// NB: Legacy API for older code.
impl UserBookmarkEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::User => "user",
Self::TtsModel => "tts_model",
Self::TtsResult => "tts_result",
Self::W2lTemplate => "w2l_template",
Self::W2lResult => "w2l_result",
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
Self::VoiceConversionModel => "voice_conversion_model",
Self::ZsVoice => "zs_voice",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"user" => Ok(Self::User),
"tts_model" => Ok(Self::TtsModel),
"tts_result" => Ok(Self::TtsResult),
"w2l_template" => Ok(Self::W2lTemplate),
"w2l_result" => Ok(Self::W2lResult),
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
"voice_conversion_model" => Ok(Self::VoiceConversionModel),
"zs_voice" => Ok(Self::ZsVoice),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(UserBookmarkEntityType::User, "user");
assert_serialization(UserBookmarkEntityType::TtsModel, "tts_model");
assert_serialization(UserBookmarkEntityType::TtsResult, "tts_result");
assert_serialization(UserBookmarkEntityType::W2lTemplate, "w2l_template");
assert_serialization(UserBookmarkEntityType::W2lResult, "w2l_result");
assert_serialization(UserBookmarkEntityType::MediaFile, "media_file");
assert_serialization(UserBookmarkEntityType::ModelWeight, "model_weight");
assert_serialization(UserBookmarkEntityType::VoiceConversionModel, "voice_conversion_model");
assert_serialization(UserBookmarkEntityType::ZsVoice, "zs_voice");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(UserBookmarkEntityType::User.to_str(), "user");
assert_eq!(UserBookmarkEntityType::TtsModel.to_str(), "tts_model");
assert_eq!(UserBookmarkEntityType::TtsResult.to_str(), "tts_result");
assert_eq!(UserBookmarkEntityType::W2lTemplate.to_str(), "w2l_template");
assert_eq!(UserBookmarkEntityType::W2lResult.to_str(), "w2l_result");
assert_eq!(UserBookmarkEntityType::MediaFile.to_str(), "media_file");
assert_eq!(UserBookmarkEntityType::ModelWeight.to_str(), "model_weight");
assert_eq!(UserBookmarkEntityType::VoiceConversionModel.to_str(), "voice_conversion_model");
assert_eq!(UserBookmarkEntityType::ZsVoice.to_str(), "zs_voice");
}
#[test]
fn test_from_str() {
assert_eq!(UserBookmarkEntityType::from_str("user").unwrap(), UserBookmarkEntityType::User);
assert_eq!(UserBookmarkEntityType::from_str("tts_model").unwrap(), UserBookmarkEntityType::TtsModel);
assert_eq!(UserBookmarkEntityType::from_str("tts_result").unwrap(), UserBookmarkEntityType::TtsResult);
assert_eq!(UserBookmarkEntityType::from_str("w2l_template").unwrap(), UserBookmarkEntityType::W2lTemplate);
assert_eq!(UserBookmarkEntityType::from_str("w2l_result").unwrap(), UserBookmarkEntityType::W2lResult);
assert_eq!(UserBookmarkEntityType::from_str("media_file").unwrap(), UserBookmarkEntityType::MediaFile);
assert_eq!(UserBookmarkEntityType::from_str("model_weight").unwrap(), UserBookmarkEntityType::ModelWeight);
assert_eq!(UserBookmarkEntityType::from_str("voice_conversion_model").unwrap(), UserBookmarkEntityType::VoiceConversionModel);
assert_eq!(UserBookmarkEntityType::from_str("zs_voice").unwrap(), UserBookmarkEntityType::ZsVoice);
assert!(UserBookmarkEntityType::from_str("foo").is_err());
}
}
}
@@ -1,111 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `user_ratings` table in a `VARCHAR(32)` field named `entity_type`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, ToSchema)]
pub enum UserRatingEntityType {
/// Media files (inference results, uploads, etc.)
#[serde(rename = "media_file")]
MediaFile,
/// Model weights (modern, polymorphic, type agnostic)
#[serde(rename = "model_weight")]
ModelWeight,
/// TTS model (architecture does not matter)
#[serde(rename = "tts_model")]
TtsModel,
/// TTS result (architecture does not matter)
#[serde(rename = "tts_result")]
TtsResult,
/// W2L template
#[serde(rename = "w2l_template")]
W2lTemplate,
/// W2L result
#[serde(rename = "w2l_result")]
W2lResult,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(UserRatingEntityType);
impl_mysql_enum_coders!(UserRatingEntityType);
/// NB: Legacy API for older code.
impl UserRatingEntityType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaFile => "media_file",
Self::ModelWeight => "model_weight",
Self::TtsModel => "tts_model",
Self::TtsResult => "tts_result",
Self::W2lTemplate => "w2l_template",
Self::W2lResult => "w2l_result",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_file" => Ok(Self::MediaFile),
"model_weight" => Ok(Self::ModelWeight),
"tts_model" => Ok(Self::TtsModel),
"tts_result" => Ok(Self::TtsResult),
"w2l_template" => Ok(Self::W2lTemplate),
"w2l_result" => Ok(Self::W2lResult),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::user_ratings::entity_type::UserRatingEntityType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(UserRatingEntityType::MediaFile, "media_file");
assert_serialization(UserRatingEntityType::ModelWeight, "model_weight");
assert_serialization(UserRatingEntityType::TtsModel, "tts_model");
assert_serialization(UserRatingEntityType::TtsResult, "tts_result");
assert_serialization(UserRatingEntityType::W2lTemplate, "w2l_template");
assert_serialization(UserRatingEntityType::W2lResult, "w2l_result");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(UserRatingEntityType::MediaFile.to_str(), "media_file");
assert_eq!(UserRatingEntityType::ModelWeight.to_str(), "model_weight");
assert_eq!(UserRatingEntityType::TtsModel.to_str(), "tts_model");
assert_eq!(UserRatingEntityType::TtsResult.to_str(), "tts_result");
assert_eq!(UserRatingEntityType::W2lTemplate.to_str(), "w2l_template");
assert_eq!(UserRatingEntityType::W2lResult.to_str(), "w2l_result");
}
#[test]
fn test_from_str() {
assert_eq!(UserRatingEntityType::from_str("media_file").unwrap(), UserRatingEntityType::MediaFile);
assert_eq!(UserRatingEntityType::from_str("model_weight").unwrap(), UserRatingEntityType::ModelWeight);
assert_eq!(UserRatingEntityType::from_str("tts_model").unwrap(), UserRatingEntityType::TtsModel);
assert_eq!(UserRatingEntityType::from_str("tts_result").unwrap(), UserRatingEntityType::TtsResult);
assert_eq!(UserRatingEntityType::from_str("w2l_template").unwrap(), UserRatingEntityType::W2lTemplate);
assert_eq!(UserRatingEntityType::from_str("w2l_result").unwrap(), UserRatingEntityType::W2lResult);
assert!(UserRatingEntityType::from_str("foo").is_err());
}
}
}
@@ -1,2 +0,0 @@
pub mod entity_type;
pub mod rating_value;
@@ -1,163 +0,0 @@
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// UserRatingValue
///
/// - Used in the `user_ratings` table as an `ENUM` field named `rating_type`.
/// - Used in the HTTP API.
///
/// To use this in a query, the query must have type annotations.
/// See: https://www.gitmemory.com/issue/launchbadge/sqlx/1241/847154375
/// eg. preferred_tts_result_visibility as `rating_type: enums::by_table::user_ratings::rating_type::UserRatingValue`
///
/// See also: https://docs.rs/sqlx/0.4.0-beta.1/sqlx/trait.Type.html
///
/// *DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY!*
///
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
#[cfg_attr(feature = "database", derive(sqlx::Type))]
#[cfg_attr(feature = "database", sqlx(rename_all = "lowercase"))]
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[serde(rename_all = "lowercase")]
pub enum UserRatingValue {
/// This is considered a ratings "soft deletion" and does not count towards a total score.
/// This is the default rating.
Neutral,
/// This is a positive vote / upvote / like.
/// They are available to non-logged-in users as long as they have the URL.
Positive,
/// This is a negative vote / downvote / dislike.
Negative,
}
impl_enum_display_and_debug_using_to_str!(UserRatingValue);
impl Default for UserRatingValue {
fn default() -> Self { Self::Neutral }
}
/// NB: Legacy API for older code.
impl UserRatingValue {
pub fn to_str(&self) -> &'static str {
match self {
Self::Neutral => "neutral",
Self::Positive => "positive",
Self::Negative => "negative",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"neutral" => Ok(Self::Neutral),
"positive" => Ok(Self::Positive),
"negative" => Ok(Self::Negative),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::user_ratings::rating_value::UserRatingValue;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(UserRatingValue::Neutral, "neutral");
assert_serialization(UserRatingValue::Positive, "positive");
assert_serialization(UserRatingValue::Negative, "negative");
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(UserRatingValue::Neutral.to_str(), "neutral");
assert_eq!(UserRatingValue::Positive.to_str(), "positive");
assert_eq!(UserRatingValue::Negative.to_str(), "negative");
}
#[test]
fn from_str() {
assert_eq!(UserRatingValue::from_str("neutral").unwrap(), UserRatingValue::Neutral);
assert_eq!(UserRatingValue::from_str("positive").unwrap(), UserRatingValue::Positive);
assert_eq!(UserRatingValue::from_str("negative").unwrap(), UserRatingValue::Negative);
assert!(UserRatingValue::from_str("foo").is_err());
}
}
mod traits {
use super::*;
#[test]
fn default() {
assert_eq!(UserRatingValue::default(), UserRatingValue::Neutral);
}
#[test]
fn display() {
let visibility = UserRatingValue::Positive;
assert_eq!(format!("{}", visibility), "positive".to_string());
}
#[test]
fn debug() {
let visibility = UserRatingValue::Negative;
assert_eq!(format!("{:?}", visibility), "negative".to_string());
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct CompositeType {
visibility: UserRatingValue,
string: String,
}
mod serde_serialization {
use super::*;
#[test]
fn serialize() {
let expected = "\"positive\"".to_string(); // NB: Quoted
assert_eq!(expected, serde_json::to_string(&UserRatingValue::Positive).unwrap());
// Just to show this serializes the same as a string
assert_eq!(expected, serde_json::to_string("positive").unwrap());
}
#[test]
fn nested_serialize() {
let value = CompositeType { visibility: UserRatingValue::Negative, string: "bar".to_string() };
let expected = r#"{"visibility":"negative","string":"bar"}"#.to_string();
assert_eq!(expected, serde_json::to_string(&value).unwrap());
}
}
mod serde_deserialization {
use super::*;
#[test]
fn deserialize() {
let payload = "\"positive\""; // NB: Quoted
let value: UserRatingValue = serde_json::from_str(payload).unwrap();
assert_eq!(value, UserRatingValue::Positive);
}
#[test]
fn nested_deserialize() {
let payload = r#"{"visibility":"neutral","string":"bar"}"#.to_string();
let expected = CompositeType {
visibility: UserRatingValue::Neutral,
string: "bar".to_string(),
};
assert_eq!(expected, serde_json::from_str::<CompositeType>(&payload).unwrap());
}
}
}
@@ -1,3 +0,0 @@
pub mod user_feature_flag;
pub mod user_signup_method;
pub mod user_signup_source;
@@ -1,144 +0,0 @@
use std::collections::BTreeSet;
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `users` table in a `VARCHAR` field (stored as comma separated set).
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum UserFeatureFlag {
/// Grants a user the ability to list media
/// (For now, it's hidden until we get an NSFW filter.)
ExploreMedia,
/// Access to studio features
Studio,
/// Whether users are allowed to upload 3D models
#[serde(rename = "upload_3d")]
Upload3d,
/// Access to video style transfer
VideoStyleTransfer,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(UserFeatureFlag);
impl_mysql_enum_coders!(UserFeatureFlag);
impl_mysql_from_row!(UserFeatureFlag);
/// NB: Legacy API for older code.
impl UserFeatureFlag {
pub fn to_str(&self) -> &'static str {
match self {
Self::ExploreMedia => "explore_media",
Self::Studio => "studio",
Self::Upload3d => "upload_3d",
Self::VideoStyleTransfer => "video_style_transfer",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"explore_media" => Ok(Self::ExploreMedia),
"studio" => Ok(Self::Studio),
"upload_3d" => Ok(Self::Upload3d),
"video_style_transfer" => Ok(Self::VideoStyleTransfer),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::ExploreMedia,
Self::Studio,
Self::Upload3d,
Self::VideoStyleTransfer,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::users::user_feature_flag::UserFeatureFlag;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(UserFeatureFlag::ExploreMedia, "explore_media");
assert_serialization(UserFeatureFlag::Studio, "studio");
assert_serialization(UserFeatureFlag::Upload3d, "upload_3d");
assert_serialization(UserFeatureFlag::VideoStyleTransfer, "video_style_transfer");
}
#[test]
fn test_to_str() {
assert_eq!(UserFeatureFlag::ExploreMedia.to_str(), "explore_media");
assert_eq!(UserFeatureFlag::Studio.to_str(), "studio");
assert_eq!(UserFeatureFlag::Upload3d.to_str(), "upload_3d");
assert_eq!(UserFeatureFlag::VideoStyleTransfer.to_str(), "video_style_transfer");
}
#[test]
fn test_from_str() {
assert_eq!(UserFeatureFlag::from_str("explore_media").unwrap(), UserFeatureFlag::ExploreMedia);
assert_eq!(UserFeatureFlag::from_str("studio").unwrap(), UserFeatureFlag::Studio);
assert_eq!(UserFeatureFlag::from_str("upload_3d").unwrap(), UserFeatureFlag::Upload3d);
assert_eq!(UserFeatureFlag::from_str("video_style_transfer").unwrap(), UserFeatureFlag::VideoStyleTransfer);
assert!(UserFeatureFlag::from_str("foo").is_err());
}
#[test]
fn all_variants() {
let mut variants = UserFeatureFlag::all_variants();
assert_eq!(variants.len(), 4);
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::ExploreMedia));
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::Studio));
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::Upload3d));
assert_eq!(variants.pop_first(), Some(UserFeatureFlag::VideoStyleTransfer));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(UserFeatureFlag::all_variants().len(), UserFeatureFlag::iter().len());
}
#[test]
fn round_trip() {
for variant in UserFeatureFlag::all_variants() {
assert_eq!(variant, UserFeatureFlag::from_str(variant.to_str()).unwrap());
assert_eq!(variant, UserFeatureFlag::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, UserFeatureFlag::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in UserFeatureFlag::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);
}
}
}
}
@@ -1,140 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `users` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum UserSignupMethod {
/// Email + Password
EmailPassword,
/// "Sign in With Google" SSO
GoogleSignIn,
/// Stripe Checkout flow, where we provision user accounts for users with a
/// synthetic/fake email address and no password. After checkout completes,
/// the user gets a real email and password - or the user can set them.
StripeCheckout,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(UserSignupMethod);
impl_mysql_enum_coders!(UserSignupMethod);
impl_mysql_from_row!(UserSignupMethod);
/// NB: Legacy API for older code.
impl UserSignupMethod {
pub fn to_str(&self) -> &'static str {
match self {
Self::EmailPassword => "email_password",
Self::GoogleSignIn=> "google_sign_in",
Self::StripeCheckout => "stripe_checkout",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"email_password" => Ok(Self::EmailPassword),
"google_sign_in" => Ok(Self::GoogleSignIn),
"stripe_checkout" => Ok(Self::StripeCheckout),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::EmailPassword,
Self::GoogleSignIn,
Self::StripeCheckout,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::users::user_signup_method::UserSignupMethod;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(UserSignupMethod::EmailPassword, "email_password");
assert_serialization(UserSignupMethod::GoogleSignIn, "google_sign_in");
assert_serialization(UserSignupMethod::StripeCheckout, "stripe_checkout");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(UserSignupMethod::EmailPassword.to_str(), "email_password");
assert_eq!(UserSignupMethod::GoogleSignIn.to_str(), "google_sign_in");
assert_eq!(UserSignupMethod::StripeCheckout.to_str(), "stripe_checkout");
}
#[test]
fn from_str() {
assert_eq!(UserSignupMethod::from_str("email_password").unwrap(), UserSignupMethod::EmailPassword);
assert_eq!(UserSignupMethod::from_str("google_sign_in").unwrap(), UserSignupMethod::GoogleSignIn);
assert_eq!(UserSignupMethod::from_str("stripe_checkout").unwrap(), UserSignupMethod::StripeCheckout);
assert!(UserSignupMethod::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = UserSignupMethod::all_variants();
assert_eq!(variants.len(), 3);
assert_eq!(variants.pop_first(), Some(UserSignupMethod::EmailPassword));
assert_eq!(variants.pop_first(), Some(UserSignupMethod::GoogleSignIn));
assert_eq!(variants.pop_first(), Some(UserSignupMethod::StripeCheckout));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(UserSignupMethod::all_variants().len(), UserSignupMethod::iter().len());
}
#[test]
fn round_trip() {
for variant in UserSignupMethod::all_variants() {
assert_eq!(variant, UserSignupMethod::from_str(variant.to_str()).unwrap());
assert_eq!(variant, UserSignupMethod::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, UserSignupMethod::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in UserSignupMethod::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);
}
}
}
}
@@ -1,193 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `users` table in a `VARCHAR(255)` (which should be a `VARCHAR(16)`) field, `maybe_source`.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum UserSignupSource {
#[deprecated(since = "2026-01-30", note = "Use other Artcraft* enum variants instead")]
#[serde(rename = "artcraft")]
ArtCraft,
/// ArtCraft Tauri App Onboard Flow
#[serde(rename = "artcraft_app")]
ArtCraftApp,
/// artcraft.ai normal onboard flow
#[serde(rename = "artcraft_ai_web")]
ArtCraftAiWeb,
/// artcraft.ai stripe checkout flow
#[serde(rename = "artcraft_ai_s")]
ArtCraftAiStripe,
/// getartcraft.com normal onboard flow
#[serde(rename = "artcraft_get_web")]
ArtCraftGetWeb,
/// getartcraft.com stripe checkout flow
#[serde(rename = "artcraft_get_s")]
ArtCraftGetStripe,
#[serde(rename = "fakeyou")]
FakeYou,
#[serde(rename = "storyteller")]
Storyteller,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(UserSignupSource);
impl_mysql_enum_coders!(UserSignupSource);
impl_mysql_from_row!(UserSignupSource);
/// NB: Legacy API for older code.
impl UserSignupSource {
pub fn to_str(&self) -> &'static str {
match self {
Self::ArtCraft => "artcraft",
Self::ArtCraftApp => "artcraft_app",
Self::ArtCraftAiWeb => "artcraft_ai_web",
Self::ArtCraftAiStripe => "artcraft_ai_s",
Self::ArtCraftGetWeb => "artcraft_get_web",
Self::ArtCraftGetStripe => "artcraft_get_s",
Self::FakeYou => "fakeyou",
Self::Storyteller => "storyteller",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"artcraft" => Ok(Self::ArtCraft),
"artcraft_app" => Ok(Self::ArtCraftApp),
"artcraft_ai_web" => Ok(Self::ArtCraftAiWeb),
"artcraft_ai_s" => Ok(Self::ArtCraftAiStripe),
"artcraft_get_web" => Ok(Self::ArtCraftGetWeb),
"artcraft_get_s" => Ok(Self::ArtCraftGetStripe),
"fakeyou" => Ok(Self::FakeYou),
"storyteller" => Ok(Self::Storyteller),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::ArtCraft,
Self::ArtCraftApp,
Self::ArtCraftAiWeb,
Self::ArtCraftAiStripe,
Self::ArtCraftGetWeb,
Self::ArtCraftGetStripe,
Self::FakeYou,
Self::Storyteller,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::users::user_signup_source::UserSignupSource;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(UserSignupSource::ArtCraft, "artcraft");
assert_serialization(UserSignupSource::ArtCraftApp, "artcraft_app");
assert_serialization(UserSignupSource::ArtCraftAiWeb, "artcraft_ai_web");
assert_serialization(UserSignupSource::ArtCraftAiStripe, "artcraft_ai_s");
assert_serialization(UserSignupSource::ArtCraftGetWeb, "artcraft_get_web");
assert_serialization(UserSignupSource::ArtCraftGetStripe, "artcraft_get_s");
assert_serialization(UserSignupSource::FakeYou, "fakeyou");
assert_serialization(UserSignupSource::Storyteller, "storyteller");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(UserSignupSource::ArtCraft.to_str(), "artcraft");
assert_eq!(UserSignupSource::ArtCraftApp.to_str(), "artcraft_app");
assert_eq!(UserSignupSource::ArtCraftAiWeb.to_str(), "artcraft_ai_web");
assert_eq!(UserSignupSource::ArtCraftAiStripe.to_str(), "artcraft_ai_s");
assert_eq!(UserSignupSource::ArtCraftGetWeb.to_str(), "artcraft_get_web");
assert_eq!(UserSignupSource::FakeYou.to_str(), "fakeyou");
assert_eq!(UserSignupSource::Storyteller.to_str(), "storyteller");
}
#[test]
fn from_str() {
assert_eq!(UserSignupSource::from_str("artcraft").unwrap(), UserSignupSource::ArtCraft);
assert_eq!(UserSignupSource::from_str("artcraft_app").unwrap(), UserSignupSource::ArtCraftApp);
assert_eq!(UserSignupSource::from_str("artcraft_ai_web").unwrap(), UserSignupSource::ArtCraftAiWeb);
assert_eq!(UserSignupSource::from_str("artcraft_ai_s").unwrap(), UserSignupSource::ArtCraftAiStripe);
assert_eq!(UserSignupSource::from_str("artcraft_get_web").unwrap(), UserSignupSource::ArtCraftGetWeb);
assert_eq!(UserSignupSource::from_str("artcraft_get_s").unwrap(), UserSignupSource::ArtCraftGetStripe);
assert_eq!(UserSignupSource::from_str("fakeyou").unwrap(), UserSignupSource::FakeYou);
assert_eq!(UserSignupSource::from_str("storyteller").unwrap(), UserSignupSource::Storyteller);
assert!(UserSignupSource::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = UserSignupSource::all_variants();
assert_eq!(variants.len(), 8);
assert_eq!(variants.pop_first(), Some(UserSignupSource::ArtCraft));
assert_eq!(variants.pop_first(), Some(UserSignupSource::ArtCraftApp));
assert_eq!(variants.pop_first(), Some(UserSignupSource::ArtCraftAiWeb));
assert_eq!(variants.pop_first(), Some(UserSignupSource::ArtCraftAiStripe));
assert_eq!(variants.pop_first(), Some(UserSignupSource::ArtCraftGetWeb));
assert_eq!(variants.pop_first(), Some(UserSignupSource::ArtCraftGetStripe));
assert_eq!(variants.pop_first(), Some(UserSignupSource::FakeYou));
assert_eq!(variants.pop_first(), Some(UserSignupSource::Storyteller));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(UserSignupSource::all_variants().len(), UserSignupSource::iter().len());
}
#[test]
fn round_trip() {
for variant in UserSignupSource::all_variants() {
assert_eq!(variant, UserSignupSource::from_str(variant.to_str()).unwrap());
assert_eq!(variant, UserSignupSource::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, UserSignupSource::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in UserSignupSource::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);
}
}
}
}
@@ -1 +0,0 @@
pub mod voice_conversion_model_type;
@@ -1,100 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `voice_conversion_models` table in `VARCHAR(32)` field `model_type`.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum VoiceConversionModelType {
// We're skipping RVC "v1" models as "v2" are much higher quality.
// Future incompatible upgrades may deserve a different enum variant.
#[serde(rename = "rvc_v2")]
RvcV2,
#[serde(rename = "soft_vc")]
SoftVc,
#[serde(rename = "so_vits_svc")]
SoVitsSvc,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(VoiceConversionModelType);
impl_mysql_enum_coders!(VoiceConversionModelType);
/// NB: Legacy API for older code.
impl VoiceConversionModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::RvcV2 => "rvc_v2",
Self::SoftVc => "soft_vc",
Self::SoVitsSvc => "so_vits_svc",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"rvc_v2" => Ok(Self::RvcV2),
"soft_vc" => Ok(Self::SoftVc),
"so_vits_svc" => Ok(Self::SoVitsSvc),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::RvcV2,
Self::SoftVc,
Self::SoVitsSvc,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::voice_conversion_models::voice_conversion_model_type::VoiceConversionModelType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(VoiceConversionModelType::RvcV2, "rvc_v2");
assert_serialization(VoiceConversionModelType::SoftVc, "soft_vc");
assert_serialization(VoiceConversionModelType::SoVitsSvc, "so_vits_svc");
}
#[test]
fn to_str() {
assert_eq!(VoiceConversionModelType::RvcV2.to_str(), "rvc_v2");
assert_eq!(VoiceConversionModelType::SoftVc.to_str(), "soft_vc");
assert_eq!(VoiceConversionModelType::SoVitsSvc.to_str(), "so_vits_svc");
}
#[test]
fn from_str() {
assert_eq!(VoiceConversionModelType::from_str("rvc_v2").unwrap(), VoiceConversionModelType::RvcV2);
assert_eq!(VoiceConversionModelType::from_str("soft_vc").unwrap(), VoiceConversionModelType::SoftVc);
assert_eq!(VoiceConversionModelType::from_str("so_vits_svc").unwrap(), VoiceConversionModelType::SoVitsSvc);
}
#[test]
fn all_variants() {
// Static check
let mut variants = VoiceConversionModelType::all_variants();
assert_eq!(variants.len(), 3);
assert_eq!(variants.pop_first(), Some(VoiceConversionModelType::RvcV2));
assert_eq!(variants.pop_first(), Some(VoiceConversionModelType::SoftVc));
assert_eq!(variants.pop_first(), Some(VoiceConversionModelType::SoVitsSvc));
assert_eq!(variants.pop_first(), None);
// Generated check
use strum::IntoEnumIterator;
assert_eq!(VoiceConversionModelType::all_variants().len(), VoiceConversionModelType::iter().len());
}
}
@@ -1 +0,0 @@
pub mod voice_conversion_media_token_type;
@@ -1,59 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `voice_conversion_results` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum VoiceConversionMediaTokenType {
/// Media token refers to record in `media_uploads` table.
MediaUpload,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(VoiceConversionMediaTokenType);
impl_mysql_enum_coders!(VoiceConversionMediaTokenType);
/// NB: Legacy API for older code.
impl VoiceConversionMediaTokenType {
pub fn to_str(&self) -> &'static str {
match self {
Self::MediaUpload => "media_upload",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"media_upload" => Ok(Self::MediaUpload),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::voice_conversion_results::voice_conversion_media_token_type::VoiceConversionMediaTokenType;
use crate::test_helpers::assert_serialization;
#[test]
fn test_serialization() {
assert_serialization(VoiceConversionMediaTokenType::MediaUpload, "media_upload");
}
#[test]
fn test_to_str() {
assert_eq!(VoiceConversionMediaTokenType::MediaUpload.to_str(), "media_upload");
}
#[test]
fn test_from_str() {
assert_eq!(VoiceConversionMediaTokenType::from_str("media_upload").unwrap(), VoiceConversionMediaTokenType::MediaUpload);
assert!(VoiceConversionMediaTokenType::from_str("foo").is_err());
}
}
@@ -1 +0,0 @@
pub mod wallet_ledger_entry_type;
@@ -1,204 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `wallet_ledger_entries` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum WalletLedgerEntryType {
/// Wallet created
#[serde(rename = "create")]
Create,
/// Credit durable banked balance
#[serde(rename = "credit_banked")]
CreditBanked,
/// Credit monthly refill
#[serde(rename = "credit_monthly")]
CreditMonthly,
/// Deduct credits (mixed durable and monthly deduction)
#[serde(rename = "deduct_mixed")]
DeductMixed,
/// Deduct durable banked credits
#[serde(rename = "deduct_banked")]
DeductBanked,
/// Deduct monthly credits
#[serde(rename = "deduct_monthly")]
DeductMonthly,
/// Refund banked credits
#[serde(rename = "refund_banked")]
RefundBanked,
/// Support staff manually credit account
#[serde(rename = "staff_add_banked")]
StaffAddBanked,
// TODO: No clean way to do "mixed" refunds yet, and if we
// refund close to the cutoff it might be unfair. Let's
// just not do monthly refunds yet and instead credit our
// users with banked credits.
// /// Refund monthly credits
// #[serde(rename = "refund_monthly")]
// RefundMonthly,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(WalletLedgerEntryType);
impl_mysql_enum_coders!(WalletLedgerEntryType);
impl_mysql_from_row!(WalletLedgerEntryType);
/// NB: Legacy API for older code.
impl WalletLedgerEntryType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Create => "create",
Self::CreditBanked => "credit_banked",
Self::CreditMonthly => "credit_monthly",
Self::DeductMixed => "deduct_mixed",
Self::DeductBanked => "deduct_banked",
Self::DeductMonthly => "deduct_monthly",
Self::RefundBanked => "refund_banked",
Self::StaffAddBanked => "staff_add_banked",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"create" => Ok(Self::Create),
"credit_banked" => Ok(Self::CreditBanked),
"credit_monthly" => Ok(Self::CreditMonthly),
"deduct_mixed" => Ok(Self::DeductMixed),
"deduct_banked" => Ok(Self::DeductBanked),
"deduct_monthly" => Ok(Self::DeductMonthly),
"refund_banked" => Ok(Self::RefundBanked),
"staff_add_banked" => Ok(Self::StaffAddBanked),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Create,
Self::CreditBanked,
Self::CreditMonthly,
Self::DeductMixed,
Self::DeductBanked,
Self::DeductMonthly,
Self::RefundBanked,
Self::StaffAddBanked,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(WalletLedgerEntryType::Create, "create");
assert_serialization(WalletLedgerEntryType::CreditBanked, "credit_banked");
assert_serialization(WalletLedgerEntryType::CreditMonthly, "credit_monthly");
assert_serialization(WalletLedgerEntryType::DeductMixed, "deduct_mixed");
assert_serialization(WalletLedgerEntryType::DeductBanked, "deduct_banked");
assert_serialization(WalletLedgerEntryType::DeductMonthly, "deduct_monthly");
assert_serialization(WalletLedgerEntryType::RefundBanked, "refund_banked");
assert_serialization(WalletLedgerEntryType::StaffAddBanked, "staff_add_banked");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(WalletLedgerEntryType::Create.to_str(), "create");
assert_eq!(WalletLedgerEntryType::CreditBanked.to_str(), "credit_banked");
assert_eq!(WalletLedgerEntryType::CreditMonthly.to_str(), "credit_monthly");
assert_eq!(WalletLedgerEntryType::DeductMixed.to_str(), "deduct_mixed");
assert_eq!(WalletLedgerEntryType::DeductBanked.to_str(), "deduct_banked");
assert_eq!(WalletLedgerEntryType::DeductMonthly.to_str(), "deduct_monthly");
assert_eq!(WalletLedgerEntryType::RefundBanked.to_str(), "refund_banked");
assert_eq!(WalletLedgerEntryType::StaffAddBanked.to_str(), "staff_add_banked");
}
#[test]
fn from_str() {
assert_eq!(WalletLedgerEntryType::from_str("create").unwrap(), WalletLedgerEntryType::Create);
assert_eq!(WalletLedgerEntryType::from_str("credit_banked").unwrap(), WalletLedgerEntryType::CreditBanked);
assert_eq!(WalletLedgerEntryType::from_str("credit_monthly").unwrap(), WalletLedgerEntryType::CreditMonthly);
assert_eq!(WalletLedgerEntryType::from_str("deduct_mixed").unwrap(), WalletLedgerEntryType::DeductMixed);
assert_eq!(WalletLedgerEntryType::from_str("deduct_banked").unwrap(), WalletLedgerEntryType::DeductBanked);
assert_eq!(WalletLedgerEntryType::from_str("deduct_monthly").unwrap(), WalletLedgerEntryType::DeductMonthly);
assert_eq!(WalletLedgerEntryType::from_str("refund_banked").unwrap(), WalletLedgerEntryType::RefundBanked);
assert_eq!(WalletLedgerEntryType::from_str("staff_add_banked").unwrap(), WalletLedgerEntryType::StaffAddBanked);
assert!(WalletLedgerEntryType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = WalletLedgerEntryType::all_variants();
assert_eq!(variants.len(), 8);
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::Create));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::CreditBanked));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::CreditMonthly));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::DeductMixed));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::DeductBanked));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::DeductMonthly));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::RefundBanked));
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::StaffAddBanked));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(WalletLedgerEntryType::all_variants().len(), WalletLedgerEntryType::iter().len());
}
#[test]
fn round_trip() {
for variant in WalletLedgerEntryType::all_variants() {
assert_eq!(variant, WalletLedgerEntryType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, WalletLedgerEntryType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, WalletLedgerEntryType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in WalletLedgerEntryType::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);
}
}
}
}
@@ -1 +0,0 @@
pub mod rendition_status;
@@ -1,96 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the SqLite `web_scraping_targets` table in a `TEXT` field named `scraping_status`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum RenditionStatus {
#[serde(rename = "new")]
New,
#[serde(rename = "skipped")]
Skipped,
#[serde(rename = "failed")]
Failed,
#[serde(rename = "permanently_failed")]
PermanentlyFailed,
#[serde(rename = "success")]
Success,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(RenditionStatus);
impl_sqlite_enum_coders!(RenditionStatus);
/// NB: Legacy API for older code.
impl RenditionStatus {
pub fn to_str(&self) -> &'static str {
match self {
Self::New => "new",
Self::Skipped => "skipped",
Self::Failed => "failed",
Self::PermanentlyFailed => "permanently_failed",
Self::Success => "success",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"new" => Ok(Self::New),
"skipped" => Ok(Self::Skipped),
"failed" => Ok(Self::Failed),
"permanently_failed" => Ok(Self::PermanentlyFailed),
"success" => Ok(Self::Success),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::web_rendition_targets::rendition_status::RenditionStatus;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(RenditionStatus::New, "new");
assert_serialization(RenditionStatus::Skipped, "skipped");
assert_serialization(RenditionStatus::Failed, "failed");
assert_serialization(RenditionStatus::PermanentlyFailed, "permanently_failed");
assert_serialization(RenditionStatus::Success, "success");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(RenditionStatus::New.to_str(), "new");
assert_eq!(RenditionStatus::Skipped.to_str(), "skipped");
assert_eq!(RenditionStatus::Failed.to_str(), "failed");
assert_eq!(RenditionStatus::PermanentlyFailed.to_str(), "permanently_failed");
assert_eq!(RenditionStatus::Success.to_str(), "success");
}
#[test]
fn test_from_str() {
assert_eq!(RenditionStatus::from_str("new").unwrap(), RenditionStatus::New);
assert_eq!(RenditionStatus::from_str("skipped").unwrap(), RenditionStatus::Skipped);
assert_eq!(RenditionStatus::from_str("failed").unwrap(), RenditionStatus::Failed);
assert_eq!(RenditionStatus::from_str("permanently_failed").unwrap(), RenditionStatus::PermanentlyFailed);
assert_eq!(RenditionStatus::from_str("success").unwrap(), RenditionStatus::Success);
assert!(RenditionStatus::from_str("foo").is_err());
}
}
}
@@ -1 +0,0 @@
pub mod scraping_status;
@@ -1,96 +0,0 @@
use serde::Deserialize;
use serde::Serialize;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the SqLite `web_scraping_targets` table in a `TEXT` field named `scraping_status`.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum ScrapingStatus {
#[serde(rename = "new")]
New,
#[serde(rename = "skipped")]
Skipped,
#[serde(rename = "failed")]
Failed,
#[serde(rename = "permanently_failed")]
PermanentlyFailed,
#[serde(rename = "success")]
Success,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(ScrapingStatus);
impl_sqlite_enum_coders!(ScrapingStatus);
/// NB: Legacy API for older code.
impl ScrapingStatus {
pub fn to_str(&self) -> &'static str {
match self {
Self::New => "new",
Self::Skipped => "skipped",
Self::Failed => "failed",
Self::PermanentlyFailed => "permanently_failed",
Self::Success => "success",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"new" => Ok(Self::New),
"skipped" => Ok(Self::Skipped),
"failed" => Ok(Self::Failed),
"permanently_failed" => Ok(Self::PermanentlyFailed),
"success" => Ok(Self::Success),
_ => Err(format!("invalid value: {:?}", value)),
}
}
}
#[cfg(test)]
mod tests {
use crate::by_table::web_scraping_targets::scraping_status::ScrapingStatus;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(ScrapingStatus::New, "new");
assert_serialization(ScrapingStatus::Skipped, "skipped");
assert_serialization(ScrapingStatus::Failed, "failed");
assert_serialization(ScrapingStatus::PermanentlyFailed, "permanently_failed");
assert_serialization(ScrapingStatus::Success, "success");
}
}
mod impl_methods {
use super::*;
#[test]
fn test_to_str() {
assert_eq!(ScrapingStatus::New.to_str(), "new");
assert_eq!(ScrapingStatus::Skipped.to_str(), "skipped");
assert_eq!(ScrapingStatus::Failed.to_str(), "failed");
assert_eq!(ScrapingStatus::PermanentlyFailed.to_str(), "permanently_failed");
assert_eq!(ScrapingStatus::Success.to_str(), "success");
}
#[test]
fn test_from_str() {
assert_eq!(ScrapingStatus::from_str("new").unwrap(), ScrapingStatus::New);
assert_eq!(ScrapingStatus::from_str("skipped").unwrap(), ScrapingStatus::Skipped);
assert_eq!(ScrapingStatus::from_str("failed").unwrap(), ScrapingStatus::Failed);
assert_eq!(ScrapingStatus::from_str("permanently_failed").unwrap(), ScrapingStatus::PermanentlyFailed);
assert_eq!(ScrapingStatus::from_str("success").unwrap(), ScrapingStatus::Success);
assert!(ScrapingStatus::from_str("foo").is_err());
}
}
}
@@ -1,108 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `zs_voices` table in a `VARCHAR(16)` field named `encoding_type`.
///
/// This indicates what type of features are used in the embeddings.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum ZsVoiceEncodingType {
/// Encodec features
#[serde(rename = "encodec")]
Encodec,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(ZsVoiceEncodingType);
impl_mysql_enum_coders!(ZsVoiceEncodingType);
/// NB: Legacy API for older code.
impl ZsVoiceEncodingType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Encodec => "encodec",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"encodec" => Ok(Self::Encodec),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Encodec,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::zs_voices::encoding_type::ZsVoiceEncodingType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(ZsVoiceEncodingType::Encodec, "encodec");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(ZsVoiceEncodingType::Encodec.to_str(), "encodec");
}
#[test]
fn from_str() {
assert_eq!(ZsVoiceEncodingType::from_str("encodec").unwrap(), ZsVoiceEncodingType::Encodec);
assert!(ZsVoiceEncodingType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = ZsVoiceEncodingType::all_variants();
assert_eq!(variants.len(), 1);
assert_eq!(variants.pop_first(), Some(ZsVoiceEncodingType::Encodec));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(ZsVoiceEncodingType::all_variants().len(), ZsVoiceEncodingType::iter().len());
}
#[test]
fn round_trip() {
for variant in ZsVoiceEncodingType::all_variants() {
assert_eq!(variant, ZsVoiceEncodingType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, ZsVoiceEncodingType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, ZsVoiceEncodingType::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1,3 +0,0 @@
pub mod encoding_type;
pub mod model_category;
pub mod model_type;
@@ -1,108 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `zs_voices` table in a `VARCHAR(16)` field named `model_category`.
///
/// This indicates what type of features are used in the embeddings.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum ZsVoiceModelCategory {
/// TTS-type zero shot models
#[serde(rename = "tts")]
Tts,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(ZsVoiceModelCategory);
impl_mysql_enum_coders!(ZsVoiceModelCategory);
/// NB: Legacy API for older code.
impl ZsVoiceModelCategory {
pub fn to_str(&self) -> &'static str {
match self {
Self::Tts => "tts",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"tts" => Ok(Self::Tts),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::Tts,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::zs_voices::model_category::ZsVoiceModelCategory;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(ZsVoiceModelCategory::Tts, "tts");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(ZsVoiceModelCategory::Tts.to_str(), "tts");
}
#[test]
fn from_str() {
assert_eq!(ZsVoiceModelCategory::from_str("tts").unwrap(), ZsVoiceModelCategory::Tts);
assert!(ZsVoiceModelCategory::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = ZsVoiceModelCategory::all_variants();
assert_eq!(variants.len(), 1);
assert_eq!(variants.pop_first(), Some(ZsVoiceModelCategory::Tts));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(ZsVoiceModelCategory::all_variants().len(), ZsVoiceModelCategory::iter().len());
}
#[test]
fn round_trip() {
for variant in ZsVoiceModelCategory::all_variants() {
assert_eq!(variant, ZsVoiceModelCategory::from_str(variant.to_str()).unwrap());
assert_eq!(variant, ZsVoiceModelCategory::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, ZsVoiceModelCategory::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
@@ -1,116 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
/// Used in the `zs_voices` table in a `VARCHAR(16)` field named `model_type`.
///
/// This indicates what type of features are used in the embeddings.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum ZsVoiceModelType {
/// TTS-type zero shot models
#[serde(rename = "vall-e-x")]
VallEX,
#[serde(rename = "styletts2")]
StyleTTS2,
}
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
impl_enum_display_and_debug_using_to_str!(ZsVoiceModelType);
impl_mysql_enum_coders!(ZsVoiceModelType);
/// NB: Legacy API for older code.
impl ZsVoiceModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::VallEX=> "vall-e-x",
Self::StyleTTS2 => "styletts2",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"vall-e-x" => Ok(Self::VallEX),
"styletts2" => Ok(Self::StyleTTS2),
_ => Err(format!("invalid value: {:?}", value)),
}
}
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::VallEX,
Self::StyleTTS2,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::zs_voices::model_type::ZsVoiceModelType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(ZsVoiceModelType::VallEX, "vall-e-x");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(ZsVoiceModelType::VallEX.to_str(), "vall-e-x");
assert_eq!(ZsVoiceModelType::StyleTTS2.to_str(), "styletts2");
}
#[test]
fn from_str() {
assert_eq!(ZsVoiceModelType::from_str("vall-e-x").unwrap(), ZsVoiceModelType::VallEX);
assert_eq!(ZsVoiceModelType::from_str("styletts2").unwrap(), ZsVoiceModelType::StyleTTS2);
assert!(ZsVoiceModelType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = ZsVoiceModelType::all_variants();
assert_eq!(variants.len(), 2);
assert_eq!(variants.pop_first(), Some(ZsVoiceModelType::VallEX));
assert_eq!(variants.pop_first(), Some(ZsVoiceModelType::StyleTTS2));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(ZsVoiceModelType::all_variants().len(), ZsVoiceModelType::iter().len());
}
#[test]
fn round_trip() {
for variant in ZsVoiceModelType::all_variants() {
assert_eq!(variant, ZsVoiceModelType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, ZsVoiceModelType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, ZsVoiceModelType::from_str(&format!("{:?}", variant)).unwrap());
}
}
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ mod macros;
#[cfg(test)] pub mod test_helpers;
pub mod by_table;
// by_table has been migrated to enums_db and enums_api crates
pub mod common;
pub mod error;
pub mod no_table;
@@ -1,7 +1,7 @@
use std::collections::BTreeSet;
use crate::api_safe::by_table::generic_inference_jobs::frontend_failure_category_for_api_clients::FrontendFailureCategoryForApiClients;
use crate::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums_db::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use crate::error::enum_error::EnumError;
#[cfg(test)]
use strum::EnumCount;
@@ -23,8 +23,6 @@ use bucket_paths::legacy::typified_paths::public::voice_conversion_results::buck
use chrono::{DateTime, Utc};
use enums::api_safe::by_table::generic_inference_jobs::frontend_failure_category_for_api_clients::FrontendFailureCategoryForApiClients;
use enums::api_safe::by_table::generic_inference_jobs::frontend_failure_category_for_old_clients::FrontendFailureCategoryForOldClients;
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory as EnumsFrontendFailureCategory;
use enums_api::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums_api::by_table::generic_inference_jobs::inference_category::InferenceCategory;
use enums_convert::by_table::generic_inference_jobs::inference_category::inference_category_to_api;
use enums::common::job_status_plus::JobStatusPlus;
@@ -296,16 +294,10 @@ fn db_record_to_response_payload(
requires_keepalive: record.is_keepalive_required,
maybe_failure_category: record
.maybe_frontend_failure_category
.and_then(|val| {
let old = EnumsFrontendFailureCategory::from_str(val.to_str()).ok()?;
FrontendFailureCategoryForOldClients::try_from_db_enum(old)
}),
.and_then(|val| FrontendFailureCategoryForOldClients::try_from_db_enum(val)),
maybe_failure_category_updated: record
.maybe_frontend_failure_category
.and_then(|val| {
let old = EnumsFrontendFailureCategory::from_str(val.to_str()).ok()?;
Some(FrontendFailureCategoryForApiClients::from_db_enum(old))
}),
.map(|val| FrontendFailureCategoryForApiClients::from_db_enum(val)),
maybe_failure_message: record.failure_reason,
progress_percentage,
maybe_current_execution_duration_seconds,