use crate::api::BotApi;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum Format {
PlainText = 1,
Html = 2,
Markdown = 3,
Json = 4,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Text {
pub text: Option<String>,
}
impl Text {
pub fn new(data: &Value) -> Self {
Self {
text: data.get("text").and_then(|v| v.as_str()).map(String::from),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatImage {
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
pub image_id: Option<String>,
}
impl PlatImage {
pub fn new(data: &Value) -> Self {
Self {
url: data.get("url").and_then(|v| v.as_str()).map(String::from),
width: data.get("width").and_then(|v| v.as_u64()).map(|v| v as u32),
height: data
.get("height")
.and_then(|v| v.as_u64())
.map(|v| v as u32),
image_id: data
.get("image_id")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Image {
pub plat_image: PlatImage,
}
impl Image {
pub fn new(data: &Value) -> Self {
Self {
plat_image: PlatImage::new(
data.get("plat_image")
.unwrap_or(&Value::Object(serde_json::Map::new())),
),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Cover {
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
}
impl Cover {
pub fn new(data: &Value) -> Self {
Self {
url: data.get("url").and_then(|v| v.as_str()).map(String::from),
width: data.get("width").and_then(|v| v.as_u64()).map(|v| v as u32),
height: data
.get("height")
.and_then(|v| v.as_u64())
.map(|v| v as u32),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatVideo {
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
pub video_id: Option<String>,
pub cover: Cover,
}
impl PlatVideo {
pub fn new(data: &Value) -> Self {
Self {
url: data.get("url").and_then(|v| v.as_str()).map(String::from),
width: data.get("width").and_then(|v| v.as_u64()).map(|v| v as u32),
height: data
.get("height")
.and_then(|v| v.as_u64())
.map(|v| v as u32),
video_id: data
.get("video_id")
.and_then(|v| v.as_str())
.map(String::from),
cover: Cover::new(
data.get("cover")
.unwrap_or(&Value::Object(serde_json::Map::new())),
),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Video {
pub plat_video: PlatVideo,
}
impl Video {
pub fn new(data: &Value) -> Self {
Self {
plat_video: PlatVideo::new(
data.get("plat_video")
.unwrap_or(&Value::Object(serde_json::Map::new())),
),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Url {
pub url: Option<String>,
pub desc: Option<String>,
}
impl Url {
pub fn new(data: &Value) -> Self {
Self {
url: data.get("url").and_then(|v| v.as_str()).map(String::from),
desc: data.get("desc").and_then(|v| v.as_str()).map(String::from),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Elem {
pub element_type: Option<u8>,
pub text: Option<Text>,
pub image: Option<Image>,
pub video: Option<Video>,
pub url: Option<Url>,
}
impl Elem {
pub fn new(data: &Value) -> Self {
let element_type = data.get("type").and_then(|v| v.as_u64()).map(|v| v as u8);
let mut elem = Self {
element_type,
text: None,
image: None,
video: None,
url: None,
};
match element_type {
Some(1) => {
elem.text = Some(Text::new(
data.get("text")
.unwrap_or(&Value::Object(serde_json::Map::new())),
));
}
Some(2) => {
elem.image = Some(Image::new(
data.get("image")
.unwrap_or(&Value::Object(serde_json::Map::new())),
));
}
Some(3) => {
elem.video = Some(Video::new(
data.get("video")
.unwrap_or(&Value::Object(serde_json::Map::new())),
));
}
Some(4) => {
elem.url = Some(Url::new(
data.get("url")
.unwrap_or(&Value::Object(serde_json::Map::new())),
));
}
_ => {}
}
elem
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Paragraph {
pub elems: Vec<Elem>,
pub props: Option<Value>,
}
impl Paragraph {
pub fn new(data: &Value) -> Self {
let elems = data
.get("elems")
.and_then(|v| v.as_array())
.map(|arr| arr.iter().map(Elem::new).collect())
.unwrap_or_default();
Self {
elems,
props: data.get("props").cloned(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Title {
pub paragraphs: Vec<Paragraph>,
}
impl Title {
pub fn new(data: &Value) -> Self {
let paragraphs = data
.get("paragraphs")
.and_then(|v| v.as_array())
.map(|arr| arr.iter().map(Paragraph::new).collect())
.unwrap_or_default();
Self { paragraphs }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Content {
pub paragraphs: Vec<Paragraph>,
}
impl Content {
pub fn new(data: &Value) -> Self {
let paragraphs = data
.get("paragraphs")
.and_then(|v| v.as_array())
.map(|arr| arr.iter().map(Paragraph::new).collect())
.unwrap_or_default();
Self { paragraphs }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadInfo {
pub title: Title,
pub content: Content,
pub thread_id: Option<String>,
pub date_time: Option<String>,
}
impl ThreadInfo {
pub fn new(data: &Value) -> Self {
let title_data = data
.get("title")
.and_then(|v| v.as_str())
.and_then(|s| serde_json::from_str(s).ok())
.unwrap_or_default();
let content_data = data
.get("content")
.and_then(|v| v.as_str())
.and_then(|s| serde_json::from_str(s).ok())
.unwrap_or_default();
Self {
title: Title::new(&title_data),
content: Content::new(&content_data),
thread_id: data
.get("thread_id")
.and_then(|v| v.as_str())
.map(String::from),
date_time: data
.get("date_time")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct Thread {
#[serde(skip)]
api: BotApi,
pub thread_info: ThreadInfo,
pub channel_id: Option<String>,
pub guild_id: Option<String>,
pub author_id: Option<String>,
pub event_id: Option<String>,
}
impl Thread {
pub fn new(api: BotApi, event_id: Option<String>, data: &Value) -> Self {
Self {
api,
event_id,
author_id: data
.get("author_id")
.and_then(|v| v.as_str())
.map(String::from),
channel_id: data
.get("channel_id")
.and_then(|v| v.as_str())
.map(String::from),
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
thread_info: ThreadInfo::new(
data.get("thread_info")
.unwrap_or(&Value::Object(serde_json::Map::new())),
),
}
}
pub fn api(&self) -> &BotApi {
&self.api
}
}
impl std::fmt::Display for Thread {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Thread {{ channel_id: {:?}, guild_id: {:?}, author_id: {:?}, event_id: {:?} }}",
self.channel_id, self.guild_id, self.author_id, self.event_id
)
}
}
#[derive(Debug, Clone, Serialize)]
pub struct OpenThread {
#[serde(skip)]
api: BotApi,
pub channel_id: Option<String>,
pub guild_id: Option<String>,
pub author_id: Option<String>,
pub event_id: Option<String>,
}
impl OpenThread {
pub fn new(api: BotApi, data: &Value) -> Self {
Self {
api,
event_id: None,
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
channel_id: data
.get("channel_id")
.and_then(|v| v.as_str())
.map(String::from),
author_id: data
.get("author_id")
.and_then(|v| v.as_str())
.map(String::from),
}
}
pub fn api(&self) -> &BotApi {
&self.api
}
}
impl std::fmt::Display for OpenThread {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"OpenThread {{ channel_id: {:?}, guild_id: {:?}, author_id: {:?}, event_id: {:?} }}",
self.channel_id, self.guild_id, self.author_id, self.event_id
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format() {
assert_eq!(Format::PlainText as u8, 1);
assert_eq!(Format::Html as u8, 2);
assert_eq!(Format::Markdown as u8, 3);
assert_eq!(Format::Json as u8, 4);
}
#[test]
fn test_text_creation() {
let data = serde_json::json!({
"text": "Hello, world!"
});
let text = Text::new(&data);
assert_eq!(text.text, Some("Hello, world!".to_string()));
}
}