use super::{Account, Application, Attachment, Card, Emoji, Mention, Poll, Reaction};
use crate::{entities as MegalodonEntities, megalodon};
use chrono::{DateTime, Utc};
use serde::{de, Deserialize, Serialize};
#[derive(Debug, Deserialize, Clone)]
pub struct Status {
pub id: String,
pub uri: String,
pub url: Option<String>,
pub account: Account,
pub in_reply_to_id: Option<String>,
pub in_reply_to_account_id: Option<String>,
pub reblog: Option<Box<Status>>,
pub content: String,
pub created_at: DateTime<Utc>,
pub edited_at: Option<DateTime<Utc>>,
pub emojis: Vec<Emoji>,
pub replies_count: u32,
pub reblogs_count: u32,
pub favourites_count: u32,
pub reblogged: Option<bool>,
pub favourited: Option<bool>,
pub muted: Option<bool>,
pub sensitive: bool,
pub spoiler_text: String,
pub visibility: StatusVisibility,
pub media_attachments: Vec<Attachment>,
pub mentions: Vec<Mention>,
pub tags: Vec<Tag>,
pub card: Option<Card>,
pub poll: Option<Poll>,
pub application: Option<Application>,
pub language: Option<String>,
pub pinned: Option<bool>,
pub bookmarked: Option<bool>,
pub pleroma: PleromaOptions,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StatusVisibility {
Public,
Unlisted,
Private,
Direct,
}
#[derive(Debug, Deserialize, Clone)]
pub struct PleromaOptions {
pub content: Option<PleromaContent>,
pub spiler_text: Option<PleromaContent>,
pub conversation_id: Option<i64>,
pub direct_conversation_id: Option<i64>,
pub emoji_reactions: Option<Vec<Reaction>>,
pub expires_at: Option<DateTime<Utc>>,
pub in_reply_to_account_acct: Option<String>,
pub local: bool,
pub parent_visible: Option<bool>,
pub pinned_at: Option<DateTime<Utc>>,
pub thread_muted: Option<bool>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PleromaContent {
pub text_plain: String,
}
impl From<StatusVisibility> for MegalodonEntities::status::StatusVisibility {
fn from(val: StatusVisibility) -> Self {
match val {
StatusVisibility::Public => MegalodonEntities::status::StatusVisibility::Public,
StatusVisibility::Unlisted => MegalodonEntities::status::StatusVisibility::Unlisted,
StatusVisibility::Private => MegalodonEntities::status::StatusVisibility::Private,
StatusVisibility::Direct => MegalodonEntities::status::StatusVisibility::Direct,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Tag {
pub name: String,
pub url: String,
}
impl From<Tag> for MegalodonEntities::status::Tag {
fn from(val: Tag) -> Self {
MegalodonEntities::status::Tag {
name: val.name,
url: val.url,
}
}
}
impl From<Status> for MegalodonEntities::Status {
fn from(val: Status) -> Self {
let mut reblog_status: Option<Box<MegalodonEntities::Status>> = None;
let mut quoted = false;
if let Some(reblog) = val.reblog {
let rs: Status = *reblog;
if rs.content != val.content {
quoted = true;
}
reblog_status = Some(Box::new(rs.into()));
}
MegalodonEntities::Status {
id: val.id,
uri: val.uri,
url: val.url,
account: val.account.into(),
in_reply_to_id: val.in_reply_to_id,
in_reply_to_account_id: val.in_reply_to_account_id,
reblog: reblog_status,
content: val.content,
plain_content: val.pleroma.content.map(|c| c.text_plain),
created_at: val.created_at,
edited_at: val.edited_at,
emojis: val.emojis.into_iter().map(|i| i.into()).collect(),
replies_count: val.replies_count,
reblogs_count: val.reblogs_count,
favourites_count: val.favourites_count,
reblogged: val.reblogged,
favourited: val.favourited,
muted: val.muted,
sensitive: val.sensitive,
spoiler_text: val.spoiler_text,
visibility: val.visibility.into(),
media_attachments: val
.media_attachments
.into_iter()
.map(|i| i.into())
.collect(),
mentions: val.mentions.into_iter().map(|i| i.into()).collect(),
tags: val.tags.into_iter().map(|i| i.into()).collect(),
card: val.card.map(|i| i.into()),
poll: val.poll.map(|i| i.into()),
application: val.application.map(|i| i.into()),
language: val.language,
pinned: val.pinned,
emoji_reactions: val
.pleroma
.emoji_reactions
.map(|v| v.into_iter().map(|e| e.into()).collect()),
quote: quoted,
bookmarked: val.bookmarked,
}
}
}
impl From<Status> for megalodon::PostStatusOutput {
fn from(val: Status) -> Self {
megalodon::PostStatusOutput::Status(val.into())
}
}
const FIELDS: &'static [&'static str] = &["text/plain"];
impl<'de> de::Deserialize<'de> for PleromaContent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
enum Field {
TextPlain,
}
impl<'de> de::Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> de::Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("`text/plain`")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match v {
"text/plain" => Ok(Field::TextPlain),
_ => Err(de::Error::unknown_field(v, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct PleromaContentVisitor;
impl<'de> de::Visitor<'de> for PleromaContentVisitor {
type Value = PleromaContent;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct PleromaContent")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let text_plain = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
Ok(Self::Value { text_plain })
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut text_plain = None;
while let Some(key) = map.next_key()? {
match key {
Field::TextPlain => {
if text_plain.is_some() {
return Err(de::Error::duplicate_field("text_plain"));
}
text_plain = Some(map.next_value()?);
}
}
}
let text_plain =
text_plain.ok_or_else(|| de::Error::missing_field("text_plain"))?;
Ok(Self::Value { text_plain })
}
}
deserializer.deserialize_struct("PleromaContent", FIELDS, PleromaContentVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pleroma_content_deserialize() {
let text = r#"{"text/plain":"posted status example"}"#;
let r = serde_json::from_str::<PleromaContent>(text);
assert!(r.is_ok());
assert_eq!(
r.unwrap(),
PleromaContent {
text_plain: String::from("posted status example")
}
);
}
}