use std::num::NonZeroU64;
use derive_getters::{Dissolve, Getters};
use serde::{Deserialize, Deserializer};
#[cfg(feature = "serde")]
use serde::{Serialize, Serializer};
use super::{
course::CourseID,
user::{ThreadParticipant, UserID},
};
#[derive(Copy, Clone, Debug, Deserialize, Hash, PartialEq, Eq, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ReplyID(u64);
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[serde(rename_all = "lowercase")]
pub enum ReplyType {
Comment,
Answer,
}
#[derive(Clone, Debug, Deserialize, Getters, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Reply {
id: ReplyID,
user_id: UserID,
course_id: CourseID,
thread_id: ThreadID,
original_id: Option<ThreadID>,
parent_id: Option<ReplyID>,
editor_id: Option<UserID>,
number: u64,
#[serde(rename = "type")]
type_: ReplyType,
kind: String,
content: String,
document: String,
flag_count: u64,
vote_count: u64,
is_endorsed: bool,
is_anonymous: bool,
is_private: bool,
is_resolved: bool,
created_at: String,
updated_at: Option<String>,
deleted_at: Option<String>,
anonymous_id: MaybeAnonymousID,
vote: u64,
comments: Vec<Reply>,
}
#[derive(Copy, Clone, Debug, Deserialize, Hash, PartialEq, Eq, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ThreadID(u64);
impl Into<u64> for ThreadID {
fn into(self) -> u64 {
self.0
}
}
impl ThreadID {
pub async fn get(&self, client: &crate::Client) -> crate::Result<ThreadResponse> {
client.get_thread(self.clone()).await
}
}
#[derive(Copy, Clone, Debug, Deserialize, Hash, PartialEq, Eq, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct AnonymousID(NonZeroU64);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Dissolve)]
pub struct MaybeAnonymousID(Option<AnonymousID>);
impl<'de> Deserialize<'de> for MaybeAnonymousID {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let got = u64::deserialize(deserializer)?;
Ok(Self(NonZeroU64::new(got).map(AnonymousID)))
}
}
#[cfg(feature = "serde")]
impl Serialize for MaybeAnonymousID {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.map(|a| a.0.get()).unwrap_or(0).serialize(serializer)
}
}
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[serde(rename_all = "lowercase")]
pub enum ThreadType {
Question,
Announcement,
Post,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ThreadWatchStatus {
Ignoring,
NotWatching,
Watching,
}
impl<'de> Deserialize<'de> for ThreadWatchStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let opt = Option::<bool>::deserialize(deserializer)?;
Ok(match opt {
None => Self::NotWatching,
Some(false) => Self::Ignoring,
Some(true) => Self::Watching,
})
}
}
#[cfg(feature = "serde")]
impl Serialize for ThreadWatchStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match &self {
Self::NotWatching => None::<bool>.serialize(serializer),
Self::Ignoring => Some(false).serialize(serializer),
Self::Watching => Some(true).serialize(serializer),
}
}
}
#[derive(Clone, Debug, Deserialize, Getters, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct PartialThread {
id: ThreadID,
user_id: UserID,
course_id: CourseID,
original_id: Option<ThreadID>,
editor_id: Option<UserID>,
accepted_id: Option<ReplyID>,
duplicate_id: Option<ThreadID>,
number: u64,
#[serde(rename = "type")]
type_: ThreadType,
title: String,
content: String,
document: String,
category: String,
subcategory: String,
subsubcategory: String,
flag_count: u64,
star_count: u64,
view_count: u64,
unique_view_count: u64,
vote_count: u64,
reply_count: u64,
unresolved_count: u64,
is_locked: bool,
is_pinned: bool,
is_private: bool,
is_endorsed: bool,
is_answered: bool,
is_student_answered: bool,
is_staff_answered: bool,
is_archived: bool,
is_anonymous: bool,
is_megathread: bool,
anonymous_comments: bool,
approved_status: String,
created_at: String,
updated_at: Option<String>,
deleted_at: Option<String>,
pinned_at: Option<String>,
anonymous_id: MaybeAnonymousID,
vote: u64,
is_seen: bool,
is_starred: bool,
is_watched: ThreadWatchStatus,
glanced_at: Option<String>,
new_reply_count: u64,
duplicate_title: Option<String>,
user: Option<ThreadParticipant>,
}
#[derive(Clone, Debug, Deserialize, Getters, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CourseThreads {
sort_key: String,
threads: Vec<PartialThread>,
users: Vec<ThreadParticipant>,
}
#[derive(Clone, Debug, Deserialize, Getters, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Thread {
id: ThreadID,
user_id: UserID,
course_id: CourseID,
original_id: Option<ThreadID>,
editor_id: Option<UserID>,
accepted_id: Option<ReplyID>,
duplicate_id: Option<ThreadID>,
number: u64,
#[serde(rename = "type")]
type_: ThreadType,
title: String,
content: String,
document: String,
category: String,
subcategory: String,
subsubcategory: String,
flag_count: u64,
star_count: u64,
view_count: u64,
unique_view_count: u64,
vote_count: u64,
reply_count: u64,
unresolved_count: u64,
is_locked: bool,
is_pinned: bool,
is_private: bool,
is_endorsed: bool,
is_student_answered: bool,
is_staff_answered: bool,
is_archived: bool,
is_anonymous: bool,
is_megathread: bool,
anonymous_comments: bool,
approved_status: String,
created_at: String,
updated_at: Option<String>,
deleted_at: Option<String>,
pinned_at: Option<String>,
anonymous_id: MaybeAnonymousID,
vote: u64,
is_seen: bool,
is_starred: bool,
is_watched: ThreadWatchStatus,
glanced_at: Option<String>,
new_reply_count: u64,
duplicate_title: Option<String>,
answers: Vec<Reply>,
comments: Vec<Reply>,
}
#[derive(Clone, Debug, Deserialize, Getters, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ThreadResponse {
thread: Thread,
}