diff --git a/src/debug.rs b/src/debug.rs index c57afba..46f05ad 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -8,7 +8,7 @@ pub struct NotificationDisplay<'a>(pub &'a Notification); impl<'a> Display for NotificationDisplay<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let n = self.0; - match n.notification_type { + match &n.notification_type { NotificationType::Follow => { write!(f, "Follow {{ #{}, @{} }}", n.id, n.account.acct ) } @@ -33,6 +33,9 @@ impl<'a> Display for NotificationDisplay<'a> { write!(f, "Reblog {{ #{}, acct: @{}, status: -- }}", n.id, n.account.acct ) } } + NotificationType::Other(other) => { + f.write_str(&other) + } } } } @@ -52,6 +55,9 @@ impl<'a> Display for EventDisplay<'a> { Event::FiltersChanged => { write!(f, "FiltersChanged") } + Event::Heartbeat => { + write!(f, "Heartbeat") + } Event::Update(s) => { StatusDisplay(s).fmt(f) } diff --git a/src/entities/event.rs b/src/entities/event.rs index bbf5af1..b682360 100644 --- a/src/entities/event.rs +++ b/src/entities/event.rs @@ -12,4 +12,6 @@ pub enum Event { Delete(String), /// FiltersChanged event FiltersChanged, + /// Indication that the socket works (ping/pong) + Heartbeat, } diff --git a/src/entities/notification.rs b/src/entities/notification.rs index f263e91..69aa526 100644 --- a/src/entities/notification.rs +++ b/src/entities/notification.rs @@ -21,8 +21,10 @@ pub struct Notification { } /// The type of notification. -#[derive(Debug, Clone, Copy, Deserialize, PartialEq)] +#[derive(Debug, Clone, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] +// Deserialize "Other" trick from https://github.com/serde-rs/serde/issues/912#issuecomment-803097289 +#[serde(remote = "NotificationType")] pub enum NotificationType { /// Someone mentioned the application client in another status. Mention, @@ -32,4 +34,30 @@ pub enum NotificationType { Favourite, /// Someone followed the application client. Follow, + /// Anything else + #[serde(skip_deserializing)] + Other(String), +} + +use std::str::FromStr; +use serde::de::{value, Deserializer, IntoDeserializer}; + +impl FromStr for NotificationType { + type Err = value::Error; + + fn from_str(s: &str) -> Result { + Self::deserialize(s.into_deserializer()) + } +} + +impl<'de> Deserialize<'de> for NotificationType { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> + { + let s = String::deserialize(deserializer)?; + let deserialized = Self::from_str(&s).unwrap_or_else(|_| { + Self::Other(s) + }); + Ok(deserialized) + } } diff --git a/src/lib.rs b/src/lib.rs index b5a3cf9..2dce72c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ use reqwest::multipart; use entities::prelude::*; pub use errors::{Error, Result}; use helpers::deserialise_response; +use serde::{Serialize,Deserialize}; use requests::{ AddFilterRequest, AddPushRequest, @@ -87,6 +88,14 @@ impl From for FediClient { } } +#[derive(Serialize,Deserialize,Clone,Copy,PartialEq)] +#[serde(rename_all="snake_case")] +pub enum SearchType { + Accounts, + Hashtags, + Statuses +} + impl FediClient { methods![get, put, post, delete,]; @@ -154,7 +163,15 @@ impl FediClient { route_v1!((get) get_filters: "filters" => Vec); route_v1!((get) get_follow_suggestions: "suggestions" => Vec); - route_v2!((get (q: &'a str, resolve: bool,)) search_v2: "search" => SearchResultV2); + route_v2!((get ( + q: &'a str, + resolve: bool, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "type")] + type_: Option, + limit: Option, + following: bool, + )) search_v2: "search" => SearchResultV2); route_v1_id!((get) get_account: "accounts/{}" => Account); route_v1_id!((post) follow: "accounts/{}/follow" => Relationship); diff --git a/src/macros.rs b/src/macros.rs index 3973f72..5fadbb1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -78,7 +78,7 @@ macro_rules! route_v1_paged { } macro_rules! route_v2 { - ((get ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty) => { + ((get ($($(#[$m:meta])* $param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( "Equivalent to `get /api/v2/",$url,"`\n", @@ -92,6 +92,9 @@ macro_rules! route_v2 { #[derive(Serialize)] struct Data<'a> { $( + $( + #[$m] + )* $param: $typ, )* #[serde(skip)] diff --git a/src/streaming.rs b/src/streaming.rs index d865043..7638b4f 100644 --- a/src/streaming.rs +++ b/src/streaming.rs @@ -91,6 +91,7 @@ impl EventReader { } pub async fn send_ping(&mut self) -> std::result::Result<(), tokio_tungstenite::tungstenite::Error> { + trace!("Sending ping"); self.stream.send(Message::Ping("pleroma groups".as_bytes().to_vec())).await } } @@ -111,20 +112,23 @@ impl Stream for EventReader { if let Ok(event) = self.make_event(&self.lines) { trace!("Parsed event"); self.lines.clear(); - return Poll::Ready(Some(event)); + Poll::Ready(Some(event)) } else { trace!("Failed to parse"); - return Poll::Pending; + Poll::Pending } } - Poll::Ready(Some(Ok(Message::Ping(_)))) | Poll::Ready(Some(Ok(Message::Pong(_)))) => { - // Discard - trace!("Ping/Pong, discard"); - Poll::Pending + Poll::Ready(Some(Ok(Message::Ping(_)))) => { + trace!("Ping"); + Poll::Ready(Some(Event::Heartbeat)) + } + Poll::Ready(Some(Ok(Message::Pong(_)))) => { + trace!("Pong"); + Poll::Ready(Some(Event::Heartbeat)) } Poll::Ready(Some(Ok(Message::Binary(_)))) => { warn!("Unexpected binary msg"); - Poll::Pending + Poll::Ready(Some(Event::Heartbeat)) } Poll::Ready(Some(Ok(Message::Close(_)))) => { warn!("Websocket close frame!");