#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MediaType {
Statusbar,
Background,
Border,
Font,
Sound,
}
impl MediaType {
pub fn folder_name(&self) -> &'static str {
match self {
Self::Statusbar => "statusbar",
Self::Background => "background",
Self::Border => "border",
Self::Font => "font",
Self::Sound => "sound",
}
}
pub fn lsm_type(&self) -> &'static str {
match self {
Self::Statusbar => "statusbar",
Self::Background => "background",
Self::Border => "border",
Self::Font => "font",
Self::Sound => "sound",
}
}
pub fn accepted_extensions(&self) -> &'static [&'static str] {
match self {
Self::Statusbar | Self::Background | Self::Border => &[".tga", ".png", ".webp", ".jpg", ".jpeg", ".blp"],
Self::Font => &[".ttf", ".otf"],
Self::Sound => &[".ogg", ".mp3", ".wav"],
}
}
pub fn output_extension(&self) -> &'static str {
match self {
Self::Statusbar | Self::Background | Self::Border => ".tga",
Self::Font => "",
Self::Sound => ".ogg",
}
}
pub fn supports_locale(&self) -> bool {
matches!(self, Self::Font)
}
}
impl std::fmt::Display for MediaType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.lsm_type())
}
}
impl std::str::FromStr for MediaType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"statusbar" => Ok(Self::Statusbar),
"background" => Ok(Self::Background),
"border" => Ok(Self::Border),
"font" => Ok(Self::Font),
"sound" => Ok(Self::Sound),
_ => Err(format!("Unknown media type: {s}")),
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Default)]
pub struct EntryMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub image_width: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_height: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_family: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_style: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_is_monospace: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_num_glyphs: Option<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub locales: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_duration_secs: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_sample_rate: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_channels: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MediaEntry {
pub id: uuid::Uuid,
#[serde(rename = "type")]
pub media_type: MediaType,
pub key: String,
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub original_name: Option<String>,
pub imported_at: chrono::DateTime<chrono::Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub checksum: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<EntryMetadata>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_media_type_parse_case_insensitive() {
assert_eq!("statusbar".parse::<MediaType>().unwrap(), MediaType::Statusbar);
assert_eq!("STATUSBAR".parse::<MediaType>().unwrap(), MediaType::Statusbar);
assert_eq!("Font".parse::<MediaType>().unwrap(), MediaType::Font);
}
#[test]
fn test_media_type_parse_invalid() {
let result = "video".parse::<MediaType>();
assert!(result.is_err());
assert!(result.unwrap_err().contains("Unknown media type"));
}
#[test]
fn test_media_type_extension_contracts() {
assert_eq!(MediaType::Statusbar.output_extension(), ".tga");
assert_eq!(MediaType::Background.output_extension(), ".tga");
assert_eq!(MediaType::Border.output_extension(), ".tga");
assert_eq!(MediaType::Font.output_extension(), "");
assert_eq!(MediaType::Sound.output_extension(), ".ogg");
assert!(MediaType::Statusbar.accepted_extensions().contains(&".png"));
assert!(MediaType::Statusbar.accepted_extensions().contains(&".blp"));
assert!(MediaType::Font.accepted_extensions().contains(&".ttf"));
assert!(MediaType::Sound.accepted_extensions().contains(&".wav"));
}
#[test]
fn test_media_type_locale_support_contract() {
assert!(!MediaType::Statusbar.supports_locale());
assert!(!MediaType::Background.supports_locale());
assert!(!MediaType::Border.supports_locale());
assert!(MediaType::Font.supports_locale());
assert!(!MediaType::Sound.supports_locale());
}
}