use super::{serde_, user_::UserCompact, Cursor};
use crate::{prelude::Username, request::GetUser, Osu, OsuResult};
use serde::{Deserialize, Serialize};
use std::fmt;
use time::OffsetDateTime;
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "rkyv", derive(Archive, RkyvDeserialize, RkyvSerialize))]
pub struct Comment {
#[serde(rename = "id")]
pub comment_id: u32,
pub commentable_id: u32,
pub commentable_type: String,
#[serde(with = "serde_::datetime")]
#[cfg_attr(feature = "rkyv", with(super::rkyv_impls::DateTimeWrapper))]
pub created_at: OffsetDateTime,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "serde_::option_datetime"
)]
#[cfg_attr(feature = "rkyv", with(super::rkyv_impls::DateTimeMap))]
pub deleted_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "serde_::option_datetime"
)]
#[cfg_attr(feature = "rkyv", with(super::rkyv_impls::DateTimeMap))]
pub edited_at: Option<OffsetDateTime>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub edited_by_id: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "rkyv", with(super::rkyv_impls::UsernameMap))]
pub legacy_name: Option<Username>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_html: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_id: Option<u32>,
pub pinned: bool,
pub replies_count: u32,
#[serde(with = "serde_::datetime")]
#[cfg_attr(feature = "rkyv", with(super::rkyv_impls::DateTimeWrapper))]
pub updated_at: OffsetDateTime,
pub user_id: Option<u32>,
pub votes_count: u32,
}
impl Comment {
#[inline]
pub fn get_user<'o>(&self, osu: &'o Osu) -> Option<GetUser<'o>> {
self.user_id.map(|id| osu.user(id))
}
}
impl PartialEq for Comment {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.comment_id == other.comment_id && self.user_id == other.user_id
}
}
impl Eq for Comment {}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct CommentBundle {
pub commentable_meta: Vec<CommentableMeta>,
pub comments: Vec<Comment>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) cursor: Option<Cursor>,
pub(crate) has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub has_more_id: Option<u32>,
pub included_comments: Vec<Comment>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pinned_comments: Option<Vec<Comment>>,
pub sort: CommentSort,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_level_count: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<u32>,
pub user_follow: bool,
pub user_votes: Vec<u32>,
pub users: Vec<UserCompact>,
}
impl CommentBundle {
#[inline]
pub fn has_more(&self) -> bool {
self.has_more
}
#[inline]
pub async fn get_next(&self, osu: &Osu) -> Option<OsuResult<CommentBundle>> {
debug_assert!(self.has_more == self.cursor.is_some());
Some(osu.comments().cursor(self.cursor.clone()?).await)
}
}
#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(
feature = "rkyv",
derive(Archive, RkyvDeserialize, RkyvSerialize),
archive(as = "Self")
)]
pub enum CommentSort {
#[serde(rename = "new")]
New,
#[serde(rename = "old")]
Old,
#[serde(rename = "top")]
Top,
}
impl fmt::Display for CommentSort {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sort = match self {
Self::New => "new",
Self::Old => "old",
Self::Top => "top",
};
f.write_str(sort)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "rkyv", derive(Archive, RkyvDeserialize, RkyvSerialize))]
#[serde(untagged)]
pub enum CommentableMeta {
Full {
id: u32,
#[serde(rename = "type")]
kind: String,
owner_id: u32,
owner_title: String,
title: String,
url: String,
},
Title {
title: String,
},
}