commit
						6fb63800d7
					
				|  | @ -62,6 +62,9 @@ optional = true | |||
| version = "1.22.0" | ||||
| features = ["rt-multi-thread", "macros"] | ||||
| 
 | ||||
| [dependencies.tokio-util] | ||||
| version = "0.7.4" | ||||
| features = ["io"] | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| tokio-test = "0.4.2" | ||||
|  |  | |||
							
								
								
									
										48
									
								
								README.md
								
								
								
								
							
							
						
						
									
										48
									
								
								README.md
								
								
								
								
							|  | @ -2,12 +2,11 @@ | |||
| 
 | ||||
| ## A Wrapper for the Mastodon API. | ||||
| 
 | ||||
| [](https://travis-ci.org/pwoolcoc/elefren) | ||||
| [](https://ci.appveyor.com/project/pwoolcoc/elefren) | ||||
| [](https://coveralls.io/github/pwoolcoc/elefren?branch=master) | ||||
| [](https://crates.io/crates/elefren) | ||||
| [](https://docs.rs/elefren) | ||||
| [](https://crates.io/crates/elefren) | ||||
| [](https://travis-ci.org/pwoolcoc/elefren) | ||||
| <!-- [](https://coveralls.io/github/pwoolcoc/elefren?branch=master) --> | ||||
| <!-- [](https://crates.io/crates/elefren) --> | ||||
| <!-- [](https://docs.rs/elefren) --> | ||||
| <!-- [](https://crates.io/crates/elefren) --> | ||||
| 
 | ||||
| [Documentation](https://docs.rs/elefren/) | ||||
| 
 | ||||
|  | @ -46,14 +45,15 @@ use elefren::prelude::*; | |||
| use elefren::helpers::toml; // requires `features = ["toml"]` | ||||
| use elefren::helpers::cli; | ||||
| 
 | ||||
| fn main() -> Result<(), Box<dyn Error>> { | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn Error>> { | ||||
|     let mastodon = if let Ok(data) = toml::from_file("mastodon-data.toml") { | ||||
|         Mastodon::from(data) | ||||
|     } else { | ||||
|         register()? | ||||
|     }; | ||||
| 
 | ||||
|     let you = mastodon.verify_credentials()?; | ||||
|     let you = mastodon.verify_credentials().await?; | ||||
| 
 | ||||
|     println!("{:#?}", you); | ||||
| 
 | ||||
|  | @ -81,25 +81,21 @@ use elefren::entities::event::Event; | |||
| 
 | ||||
| use std::error::Error; | ||||
| 
 | ||||
| fn main() -> Result<(), Box<Error>> { | ||||
|     let data = Data { | ||||
|       base: "".into(), | ||||
|       client_id: "".into(), | ||||
|       client_secret: "".into(), | ||||
|       redirect: "".into(), | ||||
|       token: "".into(), | ||||
|     }; | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<Error>> { | ||||
|     let client = Mastodon::from(Data::default()); | ||||
| 
 | ||||
|     let client = Mastodon::from(data); | ||||
| 
 | ||||
|     for event in client.streaming_user()? { | ||||
|         match event { | ||||
|             Event::Update(ref status) => { /* .. */ }, | ||||
|             Event::Notification(ref notification) => { /* .. */ }, | ||||
|             Event::Delete(ref id) => { /* .. */ }, | ||||
|             Event::FiltersChanged => { /* .. */ }, | ||||
|         } | ||||
|     } | ||||
|     client.stream_user() | ||||
|         .await? | ||||
|         .try_for_each(|event| { | ||||
|             match event { | ||||
|                 Event::Update(ref status) => { /* .. */ }, | ||||
|                 Event::Notification(ref notification) => { /* .. */ }, | ||||
|                 Event::Delete(ref id) => { /* .. */ }, | ||||
|                 Event::FiltersChanged => { /* .. */ }, | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|     Ok(()) | ||||
| } | ||||
| ``` | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ use serde_urlencoded::ser::Error as UrlEncodedError; | |||
| use tomlcrate::de::Error as TomlDeError; | ||||
| #[cfg(feature = "toml")] | ||||
| use tomlcrate::ser::Error as TomlSerError; | ||||
| use tungstenite::{error::Error as WebSocketError, Message as WebSocketMessage}; | ||||
| use url::ParseError as UrlError; | ||||
| 
 | ||||
| /// Convience type over `std::result::Result` with `Error` as the error type.
 | ||||
|  | @ -62,16 +61,12 @@ pub enum Error { | |||
|     Envy(EnvyError), | ||||
|     /// Error serializing to a query string
 | ||||
|     SerdeQs(SerdeQsError), | ||||
|     /// WebSocket error
 | ||||
|     WebSocket(WebSocketError), | ||||
|     /// An integer conversion was attempted, but the value didn't fit into the
 | ||||
|     /// target type.
 | ||||
|     ///
 | ||||
|     /// At the time of writing, this can only be triggered when a file is
 | ||||
|     /// larger than the system's usize allows.
 | ||||
|     IntConversion(TryFromIntError), | ||||
|     /// A stream message was received that wasn't recognized
 | ||||
|     UnrecognizedStreamMessage(WebSocketMessage), | ||||
|     /// Other errors
 | ||||
|     Other(String), | ||||
| } | ||||
|  | @ -100,14 +95,12 @@ impl error::Error for Error { | |||
|             #[cfg(feature = "env")] | ||||
|             Error::Envy(ref e) => e, | ||||
|             Error::SerdeQs(ref e) => e, | ||||
|             Error::WebSocket(ref e) => e, | ||||
|             Error::IntConversion(ref e) => e, | ||||
|             Error::Client(..) | Error::Server(..) => return None, | ||||
|             Error::ClientIdRequired => return None, | ||||
|             Error::ClientSecretRequired => return None, | ||||
|             Error::AccessTokenRequired => return None, | ||||
|             Error::MissingField(_) => return None, | ||||
|             Error::UnrecognizedStreamMessage(_) => return None, | ||||
|             Error::Other(..) => return None, | ||||
|         }) | ||||
|     } | ||||
|  | @ -157,10 +150,8 @@ from! { | |||
|     HeaderParseError => HeaderParseError, | ||||
|     #[cfg(feature = "env")] EnvyError => Envy, | ||||
|     SerdeQsError => SerdeQs, | ||||
|     WebSocketError => WebSocket, | ||||
|     String => Other, | ||||
|     TryFromIntError => IntConversion, | ||||
|     WebSocketMessage => UnrecognizedStreamMessage, | ||||
| } | ||||
| 
 | ||||
| #[macro_export] | ||||
|  |  | |||
|  | @ -1,61 +1,49 @@ | |||
| use std::io; | ||||
| 
 | ||||
| use crate::{ | ||||
|     entities::{event::Event, prelude::Notification, status::Status}, | ||||
|     errors::Result, | ||||
|     Error, | ||||
| }; | ||||
| use futures::{stream::try_unfold, TryStream}; | ||||
| use futures::{stream::try_unfold, TryStream, TryStreamExt}; | ||||
| use log::{as_debug, as_serde, debug, error, info, trace}; | ||||
| use tungstenite::Message; | ||||
| use reqwest::Response; | ||||
| use tokio::io::AsyncBufReadExt; | ||||
| use tokio_util::io::StreamReader; | ||||
| 
 | ||||
| /// Returns a stream of events at the given url location.
 | ||||
| /// Return a stream of events from the given response by parsing Server-Sent
 | ||||
| /// Events as they come in.
 | ||||
| ///
 | ||||
| /// See <https://docs.joinmastodon.org/methods/streaming/> for more info
 | ||||
| pub fn event_stream( | ||||
|     response: Response, | ||||
|     location: String, | ||||
| ) -> Result<impl TryStream<Ok = Event, Error = Error, Item = Result<Event>>> { | ||||
|     trace!(location = location; "connecting to websocket for events"); | ||||
|     let (client, response) = tungstenite::connect(&location)?; | ||||
|     let status = response.status(); | ||||
|     if !status.is_success() { | ||||
|         error!( | ||||
|             status = as_debug!(status), | ||||
|             body = response.body().as_ref().map(|it| String::from_utf8_lossy(it.as_slice())).unwrap_or("(empty body)".into()), | ||||
|             location = &location; | ||||
|             "error connecting to websocket" | ||||
|         ); | ||||
|         return Err(Error::Api(crate::ApiError { | ||||
|             error: status.canonical_reason().map(String::from), | ||||
|             error_description: None, | ||||
|         })); | ||||
|     } | ||||
|     debug!(location = &location, status = as_debug!(status); "successfully connected to websocket"); | ||||
|     Ok(try_unfold((client, location), |mut this| async move { | ||||
|         let (ref mut client, ref location) = this; | ||||
| ) -> impl TryStream<Ok = Event, Error = Error> { | ||||
|     let stream = StreamReader::new(response.bytes_stream().map_err(|err| { | ||||
|         error!(err = as_debug!(err); "error reading stream"); | ||||
|         io::Error::new(io::ErrorKind::BrokenPipe, format!("{err:?}")) | ||||
|     })); | ||||
|     let lines_iter = stream.lines(); | ||||
|     try_unfold((lines_iter, location), |mut this| async move { | ||||
|         let (ref mut lines_iter, ref location) = this; | ||||
|         let mut lines = vec![]; | ||||
|         loop { | ||||
|             match client.read_message() { | ||||
|                 Ok(Message::Text(line)) => { | ||||
|                     debug!(message = line, location = &location; "received websocket message"); | ||||
|                     let line = line.trim().to_string(); | ||||
|                     if line.starts_with(":") || line.is_empty() { | ||||
|                         continue; | ||||
|                     } | ||||
|                     lines.push(line); | ||||
|                     if let Ok(event) = make_event(&lines) { | ||||
|                         info!(event = as_serde!(event), location = location; "received websocket event"); | ||||
|                         lines.clear(); | ||||
|                         return Ok(Some((event, this))); | ||||
|                     } else { | ||||
|                         continue; | ||||
|                     } | ||||
|                 }, | ||||
|                 Ok(Message::Ping(data)) => { | ||||
|                     debug!(metadata = as_serde!(data); "received ping, ponging"); | ||||
|                     client.write_message(Message::Pong(data))?; | ||||
|                 }, | ||||
|                 Ok(message) => return Err(message.into()), | ||||
|                 Err(err) => return Err(err.into()), | ||||
|         while let Some(line) = lines_iter.next_line().await? { | ||||
|             debug!(message = line, location = &location; "received message"); | ||||
|             let line = line.trim().to_string(); | ||||
|             if line.starts_with(":") || line.is_empty() { | ||||
|                 continue; | ||||
|             } | ||||
|             lines.push(line); | ||||
|             if let Ok(event) = make_event(&lines) { | ||||
|                 info!(event = as_serde!(event), location = location; "received event"); | ||||
|                 lines.clear(); | ||||
|                 return Ok(Some((event, this))); | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|     })) | ||||
|         Ok(None) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| fn make_event(lines: &[String]) -> Result<Event> { | ||||
|  | @ -78,7 +66,7 @@ fn make_event(lines: &[String]) -> Result<Event> { | |||
|         data = message.payload; | ||||
|     } | ||||
|     let event: &str = &event; | ||||
|     trace!(event = event, payload = data; "websocket message parsed"); | ||||
|     trace!(event = event, payload = data; "SSE message parsed"); | ||||
|     Ok(match event { | ||||
|         "notification" => { | ||||
|             let data = data | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ | |||
| //! let data = Data::default();
 | ||||
| //! let client = Mastodon::from(data);
 | ||||
| //! tokio_test::block_on(async {
 | ||||
| //!     let stream = client.streaming_user().await.unwrap();
 | ||||
| //!     let stream = client.stream_user().await.unwrap();
 | ||||
| //!     stream.try_for_each(|event| async move {
 | ||||
| //!         match event {
 | ||||
| //!             Event::Update(ref status) => { /* .. */ },
 | ||||
|  |  | |||
|  | @ -438,3 +438,91 @@ macro_rules! paged_routes_with_id { | |||
| 
 | ||||
|     () => {} | ||||
| } | ||||
| 
 | ||||
| macro_rules! streaming { | ||||
|     ($desc:tt $fn_name:ident@$stream:literal, $($rest:tt)*) => { | ||||
|         doc_comment! { | ||||
|             concat!( | ||||
|                 $desc, | ||||
|                 "\n\nExample:\n\n", | ||||
|                 " | ||||
| use elefren::prelude::*; | ||||
| use elefren::entities::event::Event; | ||||
| use futures_util::{pin_mut, StreamExt, TryStreamExt}; | ||||
| 
 | ||||
| tokio_test::block_on(async { | ||||
|     let data = Data::default(); | ||||
|     let client = Mastodon::from(data); | ||||
|     let stream = client.",
 | ||||
|                     stringify!($fn_name), | ||||
|                     "().await.unwrap();
 | ||||
|     stream.try_for_each(|event| async move { | ||||
|         match event { | ||||
|             Event::Update(ref status) => { /* .. */ }, | ||||
|             Event::Notification(ref notification) => { /* .. */ }, | ||||
|             Event::Delete(ref id) => { /* .. */ }, | ||||
|             Event::FiltersChanged => { /* .. */ }, | ||||
|         } | ||||
|         Ok(()) | ||||
|     }).await.unwrap(); | ||||
| });" | ||||
|             ), | ||||
|             pub async fn $fn_name(&self) -> Result<impl TryStream<Ok=Event, Error=Error>> { | ||||
|                 let url = self.route(&format!("/api/v1/streaming/{}", $stream)); | ||||
|                 let response = self.authenticated(self.client.get(&url)).send().await?; | ||||
|                 debug!( | ||||
|                     status = log_serde!(response Status), url = &url, | ||||
|                     headers = log_serde!(response Headers); | ||||
|                     "received API response" | ||||
|                 ); | ||||
|                 Ok(event_stream(response.error_for_status()?, url)) | ||||
|             } | ||||
|         } | ||||
|         streaming! { $($rest)* } | ||||
|     }; | ||||
|     ($desc:tt $fn_name:ident($param:ident: $param_type:ty, like $param_doc_val:literal)@$stream:literal, $($rest:tt)*) => { | ||||
|         doc_comment! { | ||||
|             concat!( | ||||
|                 $desc, | ||||
|                 "\n\nExample:\n\n", | ||||
|                 " | ||||
| use elefren::prelude::*; | ||||
| use elefren::entities::event::Event; | ||||
| use futures_util::{pin_mut, StreamExt, TryStreamExt}; | ||||
| 
 | ||||
| tokio_test::block_on(async { | ||||
|     let data = Data::default(); | ||||
|     let client = Mastodon::from(data); | ||||
|     let stream = client.",
 | ||||
|                     stringify!($fn_name), | ||||
|                     "(", | ||||
|                     $param_doc_val, | ||||
|                     ").await.unwrap();
 | ||||
|     stream.try_for_each(|event| async move { | ||||
|         match event { | ||||
|             Event::Update(ref status) => { /* .. */ }, | ||||
|             Event::Notification(ref notification) => { /* .. */ }, | ||||
|             Event::Delete(ref id) => { /* .. */ }, | ||||
|             Event::FiltersChanged => { /* .. */ }, | ||||
|         } | ||||
|         Ok(()) | ||||
|     }).await.unwrap(); | ||||
| });" | ||||
|             ), | ||||
|             pub async fn $fn_name(&self, $param: $param_type) -> Result<impl TryStream<Ok=Event, Error=Error>> { | ||||
|                 let mut url: Url = self.route(concat!("/api/v1/streaming/", stringify!($stream))).parse()?; | ||||
|                 url.query_pairs_mut().append_pair(stringify!($param), $param.as_ref()); | ||||
|                 let url = url.to_string(); | ||||
|                 let response = self.authenticated(self.client.get(url.as_str())).send().await?; | ||||
|                 debug!( | ||||
|                     status = log_serde!(response Status), url = as_debug!(url), | ||||
|                     headers = log_serde!(response Headers); | ||||
|                     "received API response" | ||||
|                 ); | ||||
|                 Ok(event_stream(response.error_for_status()?, url)) | ||||
|             } | ||||
|         } | ||||
|         streaming! { $($rest)* } | ||||
|     }; | ||||
|     () => {} | ||||
| } | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ use crate::{ | |||
|     UpdatePushRequest, | ||||
| }; | ||||
| use futures::TryStream; | ||||
| use log::{as_debug, as_serde, debug, error, info, trace}; | ||||
| use log::{as_debug, as_serde, debug, error, trace}; | ||||
| use reqwest::{Client, RequestBuilder}; | ||||
| use url::Url; | ||||
| use uuid::Uuid; | ||||
|  | @ -129,6 +129,33 @@ impl Mastodon { | |||
|         (post) unendorse_user: "accounts/{}/unpin" => Relationship, | ||||
|     } | ||||
| 
 | ||||
|     streaming! { | ||||
|         "returns events that are relevant to the authorized user, i.e. home timeline & notifications" | ||||
|         stream_user@"user", | ||||
|         "All public posts known to the server. Analogous to the federated timeline." | ||||
|         stream_public@"public", | ||||
|         "All public posts known to the server, filtered for media attachments. Analogous to the federated timeline with 'only media' enabled." | ||||
|         stream_public_media@"public:media", | ||||
|         "All public posts originating from this server." | ||||
|         stream_local@"public:local", | ||||
|         "All public posts originating from this server, filtered for media attachments. Analogous to the local timeline with 'only media' enabled." | ||||
|         stream_local_media@"public:local:media", | ||||
|         "All public posts originating from other servers." | ||||
|         stream_remote@"public:remote", | ||||
|         "All public posts originating from other servers, filtered for media attachments." | ||||
|         stream_remote_media@"public:remote:media", | ||||
|         "All public posts using a certain hashtag." | ||||
|         stream_hashtag(tag: impl AsRef<str>, like "#bots")@"hashtag", | ||||
|         "All public posts using a certain hashtag, originating from this server." | ||||
|         stream_local_hashtag(tag: impl AsRef<str>, like "#bots")@"hashtag:local", | ||||
|         "Notifications for the current user." | ||||
|         stream_notifications@"user:notification", | ||||
|         "Updates to a specific list." | ||||
|         stream_list(list: impl AsRef<str>, like "12345")@"list", | ||||
|         "Updates to direct conversations." | ||||
|         stream_direct@"direct", | ||||
|     } | ||||
| 
 | ||||
|     /// Create a new Mastodon Client
 | ||||
|     pub fn new(client: Client, data: Data) -> Self { | ||||
|         Mastodon(Arc::new(MastodonClient { | ||||
|  | @ -368,56 +395,6 @@ impl Mastodon { | |||
|         self.following(&me.id).await | ||||
|     } | ||||
| 
 | ||||
|     /// returns events that are relevant to the authorized user, i.e. home
 | ||||
|     /// timeline & notifications
 | ||||
|     ///
 | ||||
|     /// // Example
 | ||||
|     ///
 | ||||
|     /// ```no_run
 | ||||
|     /// use elefren::prelude::*;
 | ||||
|     /// use elefren::entities::event::Event;
 | ||||
|     /// use futures_util::{pin_mut, StreamExt, TryStreamExt};
 | ||||
|     ///
 | ||||
|     /// tokio_test::block_on(async {
 | ||||
|     ///     let data = Data::default();
 | ||||
|     ///     let client = Mastodon::from(data);
 | ||||
|     ///     let stream = client.streaming_user().await.unwrap();
 | ||||
|     ///     stream.try_for_each(|event| async move {
 | ||||
|     ///         match event {
 | ||||
|     ///             Event::Update(ref status) => { /* .. */ },
 | ||||
|     ///             Event::Notification(ref notification) => { /* .. */ },
 | ||||
|     ///             Event::Delete(ref id) => { /* .. */ },
 | ||||
|     ///             Event::FiltersChanged => { /* .. */ },
 | ||||
|     ///         }
 | ||||
|     ///         Ok(())
 | ||||
|     ///     }).await.unwrap();
 | ||||
|     /// });
 | ||||
|     /// ```
 | ||||
|     pub async fn streaming_user(&self) -> Result<impl TryStream<Ok = Event, Error = Error>> { | ||||
|         let call_id = Uuid::new_v4(); | ||||
|         let mut url: Url = self.route("/api/v1/streaming").parse()?; | ||||
|         url.query_pairs_mut() | ||||
|             .append_pair("access_token", &self.data.token) | ||||
|             .append_pair("stream", "user"); | ||||
|         debug!( | ||||
|             url = url.as_str(), call_id = as_debug!(call_id); | ||||
|             "making user streaming API request" | ||||
|         ); | ||||
|         let response = reqwest::get(url.as_str()).await?; | ||||
|         let mut url: Url = response.url().as_str().parse()?; | ||||
|         info!( | ||||
|             url = url.as_str(), call_id = as_debug!(call_id), | ||||
|             status = response.status().as_str(); | ||||
|             "received url from streaming API request" | ||||
|         ); | ||||
|         let new_scheme = match url.scheme() { | ||||
|             "http" => "ws", | ||||
|             "https" => "wss", | ||||
|             x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), | ||||
|         }; | ||||
|         url.set_scheme(new_scheme) | ||||
|             .map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; | ||||
| 
 | ||||
|     /// Set the bearer authentication token
 | ||||
|     fn authenticated(&self, request: RequestBuilder) -> RequestBuilder { | ||||
|         request.bearer_auth(&self.data.token) | ||||
|  |  | |||
|  | @ -1,370 +0,0 @@ | |||
| use std::borrow::Cow; | ||||
| 
 | ||||
| use crate::{ | ||||
|     entities::prelude::*, | ||||
|     errors::Result, | ||||
|     http_send::{HttpSend, HttpSender}, | ||||
|     page::Page, | ||||
|     requests::{ | ||||
|         AddFilterRequest, | ||||
|         AddPushRequest, | ||||
|         StatusesRequest, | ||||
|         UpdateCredsRequest, | ||||
|         UpdatePushRequest, | ||||
|     }, | ||||
|     status_builder::NewStatus, | ||||
| }; | ||||
| 
 | ||||
| /// Represents the set of methods that a Mastodon Client can do, so that
 | ||||
| /// implementations might be swapped out for testing
 | ||||
| #[allow(unused)] | ||||
| pub trait MastodonClient<H: HttpSend = HttpSender> { | ||||
|     /// Type that wraps streaming API streams
 | ||||
|     type Stream: Iterator<Item = Event>; | ||||
| 
 | ||||
|     /// GET /api/v1/favourites
 | ||||
|     fn favourites(&self) -> Result<Page<Status, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/blocks
 | ||||
|     fn blocks(&self) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/domain_blocks
 | ||||
|     fn domain_blocks(&self) -> Result<Page<String, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/follow_requests
 | ||||
|     fn follow_requests(&self) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/timelines/home
 | ||||
|     fn get_home_timeline(&self) -> Result<Page<Status, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/custom_emojis
 | ||||
|     fn get_emojis(&self) -> Result<Page<Emoji, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/mutes
 | ||||
|     fn mutes(&self) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/notifications
 | ||||
|     fn notifications(&self) -> Result<Page<Notification, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/reports
 | ||||
|     fn reports(&self) -> Result<Page<Report, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/followers
 | ||||
|     fn followers(&self, id: &str) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/following
 | ||||
|     fn following(&self, id: &str) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/reblogged_by
 | ||||
|     fn reblogged_by(&self, id: &str) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/favourited_by
 | ||||
|     fn favourited_by(&self, id: &str) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// DELETE /api/v1/domain_blocks
 | ||||
|     fn unblock_domain(&self, domain: String) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/instance
 | ||||
|     fn instance(&self) -> Result<Instance> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/verify_credentials
 | ||||
|     fn verify_credentials(&self) -> Result<Account> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/reports
 | ||||
|     fn report(&self, account_id: &str, status_ids: Vec<&str>, comment: String) -> Result<Report> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/domain_blocks
 | ||||
|     fn block_domain(&self, domain: String) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/accounts/follow_requests/authorize
 | ||||
|     fn authorize_follow_request(&self, id: &str) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/accounts/follow_requests/reject
 | ||||
|     fn reject_follow_request(&self, id: &str) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/search
 | ||||
|     fn search<'a>(&self, q: &'a str, resolve: bool) -> Result<SearchResult> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v2/search
 | ||||
|     fn search_v2<'a>(&self, q: &'a str, resolve: bool) -> Result<SearchResultV2> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/follows
 | ||||
|     fn follows(&self, uri: Cow<'static, str>) -> Result<Account> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/media
 | ||||
|     fn media(&self, file: Cow<'static, str>) -> Result<Attachment> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/notifications/clear
 | ||||
|     fn clear_notifications(&self) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/notifications/dismiss
 | ||||
|     fn dismiss_notification(&self, id: &str) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id
 | ||||
|     fn get_account(&self, id: &str) -> Result<Account> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/accounts/:id/follow
 | ||||
|     fn follow(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/accounts/:id/unfollow
 | ||||
|     fn unfollow(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/block
 | ||||
|     fn block(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/unblock
 | ||||
|     fn unblock(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/mute
 | ||||
|     fn mute(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/unmute
 | ||||
|     fn unmute(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/notifications/:id
 | ||||
|     fn get_notification(&self, id: &str) -> Result<Notification> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id
 | ||||
|     fn get_status(&self, id: &str) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/context
 | ||||
|     fn get_context(&self, id: &str) -> Result<Context> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/card
 | ||||
|     fn get_card(&self, id: &str) -> Result<Card> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/statuses/:id/reblog
 | ||||
|     fn reblog(&self, id: &str) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/statuses/:id/unreblog
 | ||||
|     fn unreblog(&self, id: &str) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/statuses/:id/favourite
 | ||||
|     fn favourite(&self, id: &str) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/statuses/:id/unfavourite
 | ||||
|     fn unfavourite(&self, id: &str) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// DELETE /api/v1/statuses/:id
 | ||||
|     fn delete_status(&self, id: &str) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// PATCH /api/v1/accounts/update_credentials
 | ||||
|     fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/statuses
 | ||||
|     fn new_status(&self, status: NewStatus) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/timelines/public
 | ||||
|     fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/timelines/tag/:hashtag
 | ||||
|     fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/:id/statuses
 | ||||
|     fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status, H>> | ||||
|     where | ||||
|         S: Into<Option<StatusesRequest<'a>>>, | ||||
|     { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/relationships
 | ||||
|     fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/accounts/search?q=:query&limit=:limit&following=:following
 | ||||
|     fn search_accounts( | ||||
|         &self, | ||||
|         query: &str, | ||||
|         limit: Option<u64>, | ||||
|         following: bool, | ||||
|     ) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/push/subscription
 | ||||
|     fn add_push_subscription(&self, request: &AddPushRequest) -> Result<Subscription> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// PUT /api/v1/push/subscription
 | ||||
|     fn update_push_data(&self, request: &UpdatePushRequest) -> Result<Subscription> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/push/subscription
 | ||||
|     fn get_push_subscription(&self) -> Result<Subscription> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// DELETE /api/v1/push/subscription
 | ||||
|     fn delete_push_subscription(&self) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/filters
 | ||||
|     fn get_filters(&self) -> Result<Vec<Filter>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/filters
 | ||||
|     fn add_filter(&self, request: &mut AddFilterRequest) -> Result<Filter> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/filters/:id
 | ||||
|     fn get_filter(&self, id: &str) -> Result<Filter> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// PUT /api/v1/filters/:id
 | ||||
|     fn update_filter(&self, id: &str, request: &mut AddFilterRequest) -> Result<Filter> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// DELETE /api/v1/filters/:id
 | ||||
|     fn delete_filter(&self, id: &str) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/suggestions
 | ||||
|     fn get_follow_suggestions(&self) -> Result<Vec<Account>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// DELETE /api/v1/suggestions/:account_id
 | ||||
|     fn delete_from_suggestions(&self, id: &str) -> Result<Empty> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/endorsements
 | ||||
|     fn get_endorsements(&self) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/accounts/:id/pin
 | ||||
|     fn endorse_user(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// POST /api/v1/accounts/:id/unpin
 | ||||
|     fn unendorse_user(&self, id: &str) -> Result<Relationship> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// Shortcut for: `let me = client.verify_credentials(); client.followers()`
 | ||||
|     ///
 | ||||
|     /// ```no_run
 | ||||
|     /// use elefren::prelude::*;
 | ||||
|     /// let data = Data::default();
 | ||||
|     /// let client = Mastodon::from(data);
 | ||||
|     /// let follows_me = client.follows_me().unwrap();
 | ||||
|     /// ```
 | ||||
|     fn follows_me(&self) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// Shortcut for
 | ||||
|     /// `let me = client.verify_credentials(); client.following(&me.id)`
 | ||||
|     ///
 | ||||
|     /// ```no_run
 | ||||
|     /// use elefren::prelude::*;
 | ||||
|     /// let data = Data::default();
 | ||||
|     /// let client = Mastodon::from(data);
 | ||||
|     /// let follows_me = client.followed_by_me().unwrap();
 | ||||
|     /// ```
 | ||||
|     fn followed_by_me(&self) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns events that are relevant to the authorized user, i.e. home
 | ||||
|     /// timeline and notifications
 | ||||
|     fn streaming_user(&self) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns all public statuses
 | ||||
|     fn streaming_public(&self) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns all local statuses
 | ||||
|     fn streaming_local(&self) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns all public statuses for a particular hashtag
 | ||||
|     fn streaming_public_hashtag(&self, hashtag: &str) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns all local statuses for a particular hashtag
 | ||||
|     fn streaming_local_hashtag(&self, hashtag: &str) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns statuses for a list
 | ||||
|     fn streaming_list(&self, list_id: &str) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns all direct messages
 | ||||
|     fn streaming_direct(&self) -> Result<Self::Stream> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Trait that represents clients that can make unauthenticated calls to a
 | ||||
| /// mastodon instance
 | ||||
| #[allow(unused)] | ||||
| pub trait MastodonUnauthenticated<H: HttpSend> { | ||||
|     /// GET /api/v1/statuses/:id
 | ||||
|     fn get_status(&self, id: &str) -> Result<Status> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/context
 | ||||
|     fn get_context(&self, id: &str) -> Result<Context> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/card
 | ||||
|     fn get_card(&self, id: &str) -> Result<Card> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/reblogged_by
 | ||||
|     fn reblogged_by(&self, id: &str) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
|     /// GET /api/v1/statuses/:id/favourited_by
 | ||||
|     fn favourited_by(&self, id: &str) -> Result<Page<Account, H>> { | ||||
|         unimplemented!("This method was not implemented"); | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue