use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Continuity {
pub board: Board,
pub threads: Vec<Thread>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Board {
pub id: u64,
pub name: String,
pub board_sections: Vec<Section>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Section {
pub id: u64,
pub name: String,
pub order: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Thread {
pub post: Post,
pub replies: Vec<Reply>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Post {
pub id: u64,
pub authors: Vec<User>,
pub board: BoardInPost,
pub character: Option<Character>,
pub content: String,
#[serde(with = "crate::rfc3339")]
pub created_at: DateTime<Utc>,
pub description: Option<String>,
pub icon: Option<Icon>,
pub num_replies: u64,
pub section: Option<Section>,
pub section_order: u64,
pub status: String,
pub subject: String,
#[serde(with = "crate::rfc3339")]
pub tagged_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct User {
pub id: u64,
pub username: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Token {
pub token: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BoardInPost {
pub id: u64,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Character {
pub id: u64,
pub name: String,
pub screenname: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Icon {
pub id: u64,
pub keyword: Option<String>,
pub url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Reply {
pub id: u64,
pub character: Option<Character>,
pub character_name: Option<String>,
pub content: String,
#[serde(with = "crate::rfc3339")]
pub created_at: DateTime<Utc>,
pub icon: Option<Icon>,
#[serde(with = "crate::rfc3339")]
pub updated_at: DateTime<Utc>,
pub user: User,
}
mod helpers {
use std::{
collections::{BTreeSet, HashSet},
iter,
};
use crate::generate::transform;
use super::*;
impl Continuity {
pub fn created_at(&self) -> Option<DateTime<Utc>> {
self.threads.iter().map(|t| t.post.created_at).min()
}
pub fn tagged_at(&self) -> Option<DateTime<Utc>> {
self.threads.iter().map(|t| t.post.tagged_at).max()
}
pub fn authors(&self) -> Vec<User> {
self.threads
.iter()
.flat_map(|t| t.post.authors.clone())
.collect::<BTreeSet<_>>()
.into_iter()
.collect()
}
pub fn sections(&self) -> (Vec<(Section, Vec<&Thread>)>, Vec<&Thread>) {
let mut sections = self.board.board_sections.clone();
sections.sort_by_key(|s| s.order);
let sections: Vec<(Section, Vec<&Thread>)> = sections
.into_iter()
.map(|s| {
let mut threads: Vec<&Thread> = self
.threads
.iter()
.filter(|t| t.post.section.as_ref().map(|s| s.id) == Some(s.id))
.collect();
threads.sort_by_key(|t| t.post.section_order);
(s, threads)
})
.collect();
let mut sectionless_threads: Vec<&Thread> = self
.threads
.iter()
.filter(|t| t.post.section.is_none())
.collect();
sectionless_threads.sort_by_key(|t| t.post.section_order);
(sections, sectionless_threads)
}
}
impl Thread {
pub fn image_urls(&self) -> HashSet<String> {
let contents = iter::once(&self.post.content)
.chain(&self.post.description)
.chain(self.replies.iter().map(|r| &r.content));
let mut urls = HashSet::new();
for c in contents {
transform::edit_image_urls(c, |url| {
urls.insert(url.clone());
url
});
}
urls
}
}
}