Skip to main content

chan/
thread.rs

1use serde::Deserialize;
2
3use crate::post::{Attachment, Post};
4
5/// A full thread, as returned by `/{board}/thread/{no}.json`.
6#[derive(Debug, Clone, Deserialize)]
7pub struct Thread {
8    pub posts: Vec<Post>,
9}
10
11impl Thread {
12    /// The OP post, `thread.posts[0]`, present by construction.
13    pub fn op(&self) -> &Post { &self.posts[0] }
14
15    /// All non-OP posts.
16    pub fn replies(&self) -> &[Post] { &self.posts[1..] }
17
18    /// OP number. Convenience for `op().no`.
19    pub fn no(&self) -> u64 { self.op().no }
20
21    /// Whether the thread is archived. Archived threads keep their JSON but
22    /// their attachments are purged from the CDN, so image URL's 404.
23    pub fn is_archived(&self) -> bool { self.op().archived }
24
25    /// Every attachment in the thread, OP first, in post order.
26    pub fn attachments(&self) -> impl Iterator<Item = &Attachment> {
27        self.posts.iter().filter_map(|p| p.attachment.as_ref())
28    }
29
30    /// Attachments whose extension is a still image. See [`Attachment::is_image`].
31    pub fn image_attachments(&self) -> impl Iterator<Item = &Attachment> {
32        self.attachments().filter(|a| a.is_image())
33    }
34}