diff --git a/crates/_types/enums/src/by_table/media_files/media_file_animation_type.rs b/crates/_types/enums/src/by_table/media_files/media_file_animation_type.rs new file mode 100644 index 0000000000..c7372b4297 --- /dev/null +++ b/crates/_types/enums/src/by_table/media_files/media_file_animation_type.rs @@ -0,0 +1,142 @@ +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 { + /// Animation or rig for MikuMikuDance (MMD). + MikuMikuDance, + + /// Animation or rig for Mixamo + Mixamo, + + /// Animation generated by MocapNet + MocapNet, + + /// Animation or rig for Rigify. + Rigify, +} + +// 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::MikuMikuDance => "miku_miku_dance", + Self::Mixamo => "mixamo", + Self::MocapNet => "mocap_net", + Self::Rigify => "rigify", + } + } + + pub fn from_str(value: &str) -> Result { + match value { + "miku_miku_dance" => Ok(Self::MikuMikuDance), + "mixamo" => Ok(Self::Mixamo), + "mocap_net" => Ok(Self::MocapNet), + "rigify" => Ok(Self::Rigify), + _ => Err(format!("invalid value: {:?}", value)), + } + } + + pub fn all_variants() -> BTreeSet { + // NB: BTreeSet is sorted + // NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc. + BTreeSet::from([ + Self::MikuMikuDance, + Self::Mixamo, + Self::MocapNet, + Self::Rigify, + ]) + } +} + +#[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::MikuMikuDance, "miku_miku_dance"); + assert_serialization(MediaFileAnimationType::Mixamo, "mixamo"); + assert_serialization(MediaFileAnimationType::MocapNet, "mocap_net"); + assert_serialization(MediaFileAnimationType::Rigify, "rigify"); + } + + #[test] + fn test_to_str() { + assert_eq!(MediaFileAnimationType::MikuMikuDance.to_str(), "miku_miku_dance"); + assert_eq!(MediaFileAnimationType::Mixamo.to_str(), "mixamo"); + assert_eq!(MediaFileAnimationType::MocapNet.to_str(), "mocap_net"); + assert_eq!(MediaFileAnimationType::Rigify.to_str(), "rigify"); + } + + #[test] + fn test_from_str() { + assert_eq!(MediaFileAnimationType::from_str("miku_miku_dance").unwrap(), MediaFileAnimationType::MikuMikuDance); + assert_eq!(MediaFileAnimationType::from_str("mixamo").unwrap(), MediaFileAnimationType::Mixamo); + assert_eq!(MediaFileAnimationType::from_str("mocap_net").unwrap(), MediaFileAnimationType::MocapNet); + assert_eq!(MediaFileAnimationType::from_str("rigify").unwrap(), MediaFileAnimationType::Rigify); + assert!(MediaFileAnimationType::from_str("foo").is_err()); + } + + #[test] + fn all_variants() { + let mut variants = MediaFileAnimationType::all_variants(); + assert_eq!(variants.len(), 4); + assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MikuMikuDance)); + assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::Mixamo)); + assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::MocapNet)); + assert_eq!(variants.pop_first(), Some(MediaFileAnimationType::Rigify)); + 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); + } + } + } +} diff --git a/crates/_types/enums/src/by_table/media_files/mod.rs b/crates/_types/enums/src/by_table/media_files/mod.rs index 84869955e4..827ef8cb12 100644 --- a/crates/_types/enums/src/by_table/media_files/mod.rs +++ b/crates/_types/enums/src/by_table/media_files/mod.rs @@ -1,3 +1,4 @@ +pub mod media_file_animation_type; pub mod media_file_class; pub mod media_file_origin_category; pub mod media_file_origin_model_type;