Files
imks/models/message_embed.rs
zhenyi 821537186e refactor(tests): reformat code and update dependency management
- Reorganized import statements in adapter tests for better readability
- Replaced or_insert_with(Vec::new) with or_default() in test closures
- Updated Cargo.lock with new dependency versions and checksums
- Added TLS features to tonic dependency configuration
- Included sqlx, chrono, and uuid dependencies with specific features
- Added jsonwebtoken and arc-swap as project dependencies
- Reformatted assertion statements to comply with line length limits
- Adjusted base64 import order in engine codec module
- Updated protobuf include statement formatting
2026-06-11 12:11:05 +08:00

164 lines
4.8 KiB
Rust

//! Rich embed on a message — new table `message_embed` + `message_embed_field`.
//!
//! Discord-style embeds: link previews, rich cards with title/description/
//! thumbnail/image/footer/fields. Generated by the server (link preview)
//! or sent by bots/webhooks.
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
/// A single embed attached to a message.
/// One message can have multiple embeds (e.g. link preview + bot embed).
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct MessageEmbed {
pub id: Uuid,
pub message_id: Uuid,
/// "link" | "article" | "image" | "video" | "rich"
pub embed_type: String,
pub title: Option<String>,
pub description: Option<String>,
pub url: Option<String>,
/// Embed accent color as integer (Discord format: 0xRRGGBB).
pub color: Option<i32>,
// Media
/// Main image URL.
pub image_url: Option<String>,
pub image_width: Option<i32>,
pub image_height: Option<i32>,
/// Small thumbnail URL.
pub thumbnail_url: Option<String>,
pub thumbnail_width: Option<i32>,
pub thumbnail_height: Option<i32>,
/// Video URL (for video embeds).
pub video_url: Option<String>,
pub video_width: Option<i32>,
pub video_height: Option<i32>,
// Footer
pub author_name: Option<String>,
pub author_url: Option<String>,
pub author_icon_url: Option<String>,
pub footer_text: Option<String>,
pub footer_icon_url: Option<String>,
/// Provider name (e.g. "YouTube", "GitHub").
pub provider_name: Option<String>,
pub provider_url: Option<String>,
pub created_at: DateTime<Utc>,
}
/// A key-value field within an embed (Discord-style field rows).
/// Stored in a separate table for flexibility.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct MessageEmbedField {
pub id: Uuid,
pub embed_id: Uuid,
pub name: String,
pub value: String,
/// Whether this field should display inline (side by side with other inline fields).
pub inline: bool,
pub position: i32,
}
/// An embed with its fields resolved in a single structure.
///
/// Convenience type returned by read APIs, joining embed and fields.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbedDetail {
#[serde(flatten)]
pub embed: MessageEmbed,
pub fields: Vec<MessageEmbedField>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_embed_serialize() {
let embed = MessageEmbed {
id: Uuid::now_v7(),
message_id: Uuid::now_v7(),
embed_type: "link".into(),
title: Some("Example".into()),
description: Some("A link preview".into()),
url: Some("https://example.com".into()),
color: Some(0x00FF00),
image_url: None,
image_width: None,
image_height: None,
thumbnail_url: None,
thumbnail_width: None,
thumbnail_height: None,
video_url: None,
video_width: None,
video_height: None,
author_name: None,
author_url: None,
author_icon_url: None,
footer_text: None,
footer_icon_url: None,
provider_name: Some("GitHub".into()),
provider_url: None,
created_at: Utc::now(),
};
let json = serde_json::to_value(&embed).unwrap();
assert_eq!(json["embed_type"], "link");
assert_eq!(json["title"], "Example");
}
#[test]
fn test_embed_detail_serialize() {
let embed = MessageEmbed {
id: Uuid::now_v7(),
message_id: Uuid::now_v7(),
embed_type: "rich".into(),
title: Some("Article".into()),
description: None,
url: None,
color: None,
image_url: None,
image_width: None,
image_height: None,
thumbnail_url: None,
thumbnail_width: None,
thumbnail_height: None,
video_url: None,
video_width: None,
video_height: None,
author_name: None,
author_url: None,
author_icon_url: None,
footer_text: None,
footer_icon_url: None,
provider_name: None,
provider_url: None,
created_at: Utc::now(),
};
let field = MessageEmbedField {
id: Uuid::now_v7(),
embed_id: embed.id,
name: "Key".into(),
value: "Value".into(),
inline: true,
position: 0,
};
let detail = EmbedDetail {
embed,
fields: vec![field],
};
let json = serde_json::to_value(&detail).unwrap();
assert_eq!(json["embed_type"], "rich");
assert!(json["fields"].is_array());
}
}