use serde::{Deserialize, Serialize};
#[cfg(feature = "utoipa")]
use utoipa::ToSchema;
#[cfg(feature = "validator")]
use validator::Validate;
use super::{EmbedId, EmojiId, MediaId, MessageId, MessageVerId, Mime, UserId};
pub mod animated;
pub mod embed;
pub mod file;
pub mod stream;
pub mod thumb;
pub use animated::Animated;
pub use embed::Embed;
pub use file::*;
pub use stream::Streamable;
pub use thumb::{Thumb, Thumbs};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct Media<T: MediaType> {
pub id: MediaId,
pub is_deleted: bool,
pub is_likely_nsfw: bool,
pub is_quarantined: bool,
pub link: Option<MediaLink>,
pub user_id: UserId,
pub info: T,
#[cfg_attr(
feature = "utoipa",
schema(required = false, min_length = 1, max_length = 8192)
)]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 8192)))]
pub alt: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[serde(tag = "type")]
pub enum MediaLink {
Message {
message_id: MessageId,
version_id: MessageVerId,
},
AvatarUser {
user_id: UserId,
},
Embed {
embed_id: EmbedId,
},
Emoji {
emoji_id: EmojiId,
},
}
pub trait MediaType {
fn tag(&self) -> &'static str;
}
impl<T: MediaType> MediaType for File<T> {
fn tag(&self) -> &'static str {
self.meta.tag()
}
}
macro_rules! impl_media_type {
($name:ident) => {
pastey::paste! {
pub type [<Media $name>] = Media<$name>;
}
impl MediaType for $name {
fn tag(&self) -> &'static str {
stringify!($name)
}
}
};
}
impl_media_type!(Image);
impl_media_type!(Video);
impl_media_type!(Audio);
impl_media_type!(Streamable);
impl_media_type!(Text);
impl_media_type!(Generic);
impl_media_type!(Embed);
impl_media_type!(Animated);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[serde(tag = "type")]
pub enum MediaFile {
Image(Media<FileImage>),
Video(Media<FileVideo>),
Audio(Media<FileAudio>),
Text(Media<FileText>),
File(Media<FileGeneric>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[serde(tag = "type")]
pub enum MediaAny {
Image(Media<FileImage>),
Video(Media<FileVideo>),
Audio(Media<FileAudio>),
Text(Media<FileText>),
File(Media<FileGeneric>),
Streamable(Media<Streamable>),
Embed(Media<Embed>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[serde(tag = "type")]
pub struct Attachment {
pub media: MediaAny,
pub is_spoiler: bool,
pub alt_override: Option<String>,
}
#[cfg(test)]
mod tests {
use super::MediaAny;
#[test]
fn test_roundtrip() {
let val = serde_json::json!({
"type": "Image",
"id": "6f8bc7a5-a628-4a01-9bea-48b57c3b1036",
"user_id": "555509b9-edba-4a8a-a51b-f367501a4f5f",
"alt": "a test image",
"is_deleted": false,
"is_likely_nsfw": false,
"is_quarantined": false,
"link": null,
"info": {
"filename": "test.png",
"size": 1234,
"mime": "image/png",
"url": "https://example.com/",
"source_url": null,
"thumbs": [],
"height": 123,
"width": 456,
}
});
let parsed: MediaAny = serde_json::from_value(val.clone()).unwrap();
assert_eq!(serde_json::to_value(&parsed).unwrap(), val);
}
}