use std::fmt;
use serde::{Deserialize, Serializer};
use time::OffsetDateTime;
use crate::{prelude::Username, request::GetUser, Osu, OsuResult};
use super::{serde_util, user::User, CacheUserFn, ContainedUsers};
#[derive(Clone, Debug, Deserialize)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct Comment {
#[serde(rename = "id")]
pub comment_id: u32,
pub commentable_id: u32,
pub commentable_type: String,
#[serde(with = "serde_util::datetime")]
pub created_at: OffsetDateTime,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "serde_util::option_datetime"
)]
pub deleted_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "serde_util::option_datetime"
)]
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")]
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_util::datetime")]
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)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct CommentBundle {
pub commentable_meta: Vec<CommentableMeta>,
pub comments: Vec<Comment>,
#[serde(
default,
rename = "cursor_string",
skip_serializing_if = "Option::is_none"
)]
pub(crate) cursor: Option<Box<str>>,
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<User>,
}
impl CommentBundle {
#[inline]
pub const 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.as_deref()?).await)
}
}
impl ContainedUsers for CommentBundle {
fn apply_to_users(&self, f: impl CacheUserFn) {
self.users.apply_to_users(f);
}
}
#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub enum CommentSort {
#[serde(rename = "new")]
New,
#[serde(rename = "old")]
Old,
#[serde(rename = "top")]
Top,
}
impl CommentSort {
pub const fn as_str(self) -> &'static str {
match self {
Self::New => "new",
Self::Old => "old",
Self::Top => "top",
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn serialize_as_query<S: Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl fmt::Display for CommentSort {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[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,
},
}