From b6b7372ebb3e483f805082e4caefb4250b75f05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 21 Aug 2021 11:49:05 +0200 Subject: [PATCH] removed the trait so it can be easier made async --- src/lib.rs | 294 ++++++++++++++++++++------------------------------ src/macros.rs | 196 +++++++++------------------------ 2 files changed, 164 insertions(+), 326 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f69f17e..facd9ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,10 +47,10 @@ //! 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 => { /* .. */ }, +//! Event::Update(ref status) => { /* .. */ } +//! Event::Notification(ref notification) => { /* .. */ } +//! Event::Delete(ref id) => { /* .. */ } +//! Event::FiltersChanged => { /* .. */ } //! } //! } //! # Ok(()) @@ -69,28 +69,30 @@ unused_import_braces, unused_qualifications )] -#![cfg_attr(feature = "nightly", allow(broken_intra_doc_links))] -#![allow(broken_intra_doc_links)] +// #![cfg_attr(feature = "nightly", allow(broken_intra_doc_links))] +// #![allow(broken_intra_doc_links)] + +#[macro_use] +extern crate log; use std::{borrow::Cow, io::BufRead, ops}; +use std::net::TcpStream; -use reqwest::blocking::{multipart, Client, RequestBuilder, Response}; +pub use isolang::Language; +use reqwest::blocking::{Client, multipart, RequestBuilder, Response}; use tap_reader::Tap; +use tungstenite::stream::MaybeTlsStream; use crate::{entities::prelude::*, page::Page}; - pub use crate::{ data::Data, errors::{ApiError, Error, Result}, - mastodon_client::{MastodonClient, MastodonUnauthenticated}, + // mastodon_client::{MastodonClient, MastodonUnauthenticated}, media_builder::MediaBuilder, registration::Registration, requests::{AddFilterRequest, AddPushRequest, StatusesRequest, UpdateCredsRequest, UpdatePushRequest}, status_builder::{NewStatus, StatusBuilder}, }; -pub use isolang::Language; -use tungstenite::stream::MaybeTlsStream; -use std::net::TcpStream; /// Registering your App pub mod apps; @@ -117,22 +119,27 @@ pub mod scopes; pub mod status_builder; #[macro_use] mod macros; + /// Automatically import the things you need pub mod prelude { pub use crate::{ - scopes::Scopes, Data, Mastodon, MastodonClient, NewStatus, Registration, StatusBuilder, StatusesRequest, + Data, Mastodon, NewStatus, Registration, scopes::Scopes, StatusBuilder, StatusesRequest, }; } +// type Stream = EventReader; + /// Your mastodon application client, handles all requests to and from Mastodon. #[derive(Clone, Debug)] pub struct Mastodon { + client: Client, /// Raw data about your mastodon instance. pub data: Data, } impl Mastodon { + methods![get, post, delete,]; fn route(&self, url: &str) -> String { @@ -154,79 +161,70 @@ impl From for Mastodon { } } -impl MastodonClient for Mastodon { - type Stream = EventReader; - - paged_routes! { - (get) favourites: "favourites" => Status, - (get) blocks: "blocks" => Account, - (get) domain_blocks: "domain_blocks" => String, - (get) follow_requests: "follow_requests" => Account, - (get) get_home_timeline: "timelines/home" => Status, - (get) get_local_timeline: "timelines/public?local=true" => Status, - (get) get_federated_timeline: "timelines/public?local=false" => Status, - (get) get_emojis: "custom_emojis" => Emoji, - (get) mutes: "mutes" => Account, - (get) notifications: "notifications" => Notification, - (get) reports: "reports" => Report, - (get (q: &'a str, #[serde(skip_serializing_if = "Option::is_none")] limit: Option, following: bool,)) search_accounts: "accounts/search" => Account, - (get) get_endorsements: "endorsements" => Account, - } - - paged_routes_with_id! { - (get) followers: "accounts/{}/followers" => Account, - (get) following: "accounts/{}/following" => Account, - (get) reblogged_by: "statuses/{}/reblogged_by" => Account, - (get) favourited_by: "statuses/{}/favourited_by" => Account, - } - - route! { - (delete (domain: String,)) unblock_domain: "domain_blocks" => Empty, - (get) instance: "instance" => Instance, - (get) verify_credentials: "accounts/verify_credentials" => Account, - (post (account_id: &str, status_ids: Vec<&str>, comment: String,)) report: "reports" => Report, - (post (domain: String,)) block_domain: "domain_blocks" => Empty, - (post (id: &str,)) authorize_follow_request: "accounts/follow_requests/authorize" => Empty, - (post (id: &str,)) reject_follow_request: "accounts/follow_requests/reject" => Empty, - (get (q: &'a str, resolve: bool,)) search: "search" => SearchResult, - (post (uri: Cow<'static, str>,)) follows: "follows" => Account, - (post) clear_notifications: "notifications/clear" => Empty, - (post (id: &str,)) dismiss_notification: "notifications/dismiss" => Empty, - (get) get_push_subscription: "push/subscription" => Subscription, - (delete) delete_push_subscription: "push/subscription" => Empty, - (get) get_filters: "filters" => Vec, - (get) get_follow_suggestions: "suggestions" => Vec, - } - - route_v2! { - (get (q: &'a str, resolve: bool,)) search_v2: "search" => SearchResultV2, - } - - route_id! { - (get) get_account: "accounts/{}" => Account, - (post) follow: "accounts/{}/follow" => Relationship, - (post) unfollow: "accounts/{}/unfollow" => Relationship, - (post) block: "accounts/{}/block" => Relationship, - (post) unblock: "accounts/{}/unblock" => Relationship, - (get) mute: "accounts/{}/mute" => Relationship, - (get) unmute: "accounts/{}/unmute" => Relationship, - (get) get_notification: "notifications/{}" => Notification, - (get) get_status: "statuses/{}" => Status, - (get) get_context: "statuses/{}/context" => Context, - (get) get_card: "statuses/{}/card" => Card, - (post) reblog: "statuses/{}/reblog" => Status, - (post) unreblog: "statuses/{}/unreblog" => Status, - (post) favourite: "statuses/{}/favourite" => Status, - (post) unfavourite: "statuses/{}/unfavourite" => Status, - (delete) delete_status: "statuses/{}" => Empty, - (get) get_filter: "filters/{}" => Filter, - (delete) delete_filter: "filters/{}" => Empty, - (delete) delete_from_suggestions: "suggestions/{}" => Empty, - (post) endorse_user: "accounts/{}/pin" => Relationship, - (post) unendorse_user: "accounts/{}/unpin" => Relationship, - } - - fn add_filter(&self, request: &mut AddFilterRequest) -> Result { +type MastodonStream = EventReader; + +impl Mastodon { + route_v1_paged!((get) favourites: "favourites" => Status); + route_v1_paged!((get) blocks: "blocks" => Account); + route_v1_paged!((get) domain_blocks: "domain_blocks" => String); + route_v1_paged!((get) follow_requests: "follow_requests" => Account); + route_v1_paged!((get) get_home_timeline: "timelines/home" => Status); + route_v1_paged!((get) get_local_timeline: "timelines/public?local=true" => Status); + route_v1_paged!((get) get_federated_timeline: "timelines/public?local=false" => Status); + route_v1_paged!((get) get_emojis: "custom_emojis" => Emoji); + route_v1_paged!((get) mutes: "mutes" => Account); + route_v1_paged!((get) notifications: "notifications" => Notification); + route_v1_paged!((get) reports: "reports" => Report); + route_v1_paged!((get (q: &'a str, #[serde(skip_serializing_if = "Option::is_none")] limit: Option, following: bool,)) search_accounts: "accounts/search" => Account); + route_v1_paged!((get) get_endorsements: "endorsements" => Account); + + route_v1_paged_id!((get) followers: "accounts/{}/followers" => Account); + route_v1_paged_id!((get) following: "accounts/{}/following" => Account); + route_v1_paged_id!((get) reblogged_by: "statuses/{}/reblogged_by" => Account); + route_v1_paged_id!((get) favourited_by: "statuses/{}/favourited_by" => Account); + + route_v1!((delete (domain: String,)) unblock_domain: "domain_blocks" => Empty); + route_v1!((get) instance: "instance" => Instance); + route_v1!((get) verify_credentials: "accounts/verify_credentials" => Account); + route_v1!((post (account_id: &str, status_ids: Vec<&str>, comment: String,)) report: "reports" => Report); + route_v1!((post (domain: String,)) block_domain: "domain_blocks" => Empty); + route_v1!((post (id: &str,)) authorize_follow_request: "accounts/follow_requests/authorize" => Empty); + route_v1!((post (id: &str,)) reject_follow_request: "accounts/follow_requests/reject" => Empty); + route_v1!((get (q: &'a str, resolve: bool,)) search: "search" => SearchResult); + route_v1!((post (uri: Cow<'static, str>,)) follows: "follows" => Account); + route_v1!((post) clear_notifications: "notifications/clear" => Empty); + route_v1!((post (id: &str,)) dismiss_notification: "notifications/dismiss" => Empty); + route_v1!((get) get_push_subscription: "push/subscription" => Subscription); + route_v1!((delete) delete_push_subscription: "push/subscription" => Empty); + 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_v1_id!((get) get_account: "accounts/{}" => Account); + route_v1_id!((post) follow: "accounts/{}/follow" => Relationship); + route_v1_id!((post) unfollow: "accounts/{}/unfollow" => Relationship); + route_v1_id!((post) block: "accounts/{}/block" => Relationship); + route_v1_id!((post) unblock: "accounts/{}/unblock" => Relationship); + route_v1_id!((get) mute: "accounts/{}/mute" => Relationship); + route_v1_id!((get) unmute: "accounts/{}/unmute" => Relationship); + route_v1_id!((get) get_notification: "notifications/{}" => Notification); + route_v1_id!((get) get_status: "statuses/{}" => Status); + route_v1_id!((get) get_context: "statuses/{}/context" => Context); + route_v1_id!((get) get_card: "statuses/{}/card" => Card); + route_v1_id!((post) reblog: "statuses/{}/reblog" => Status); + route_v1_id!((post) unreblog: "statuses/{}/unreblog" => Status); + route_v1_id!((post) favourite: "statuses/{}/favourite" => Status); + route_v1_id!((post) unfavourite: "statuses/{}/unfavourite" => Status); + route_v1_id!((delete) delete_status: "statuses/{}" => Empty); + route_v1_id!((get) get_filter: "filters/{}" => Filter); + route_v1_id!((delete) delete_filter: "filters/{}" => Empty); + route_v1_id!((delete) delete_from_suggestions: "suggestions/{}" => Empty); + route_v1_id!((post) endorse_user: "accounts/{}/pin" => Relationship); + route_v1_id!((post) unendorse_user: "accounts/{}/unpin" => Relationship); + + /// POST /api/v1/filters + pub fn add_filter(&self, request: &mut AddFilterRequest) -> Result { let url = self.route("/api/v1/filters"); let response = self.send(self.client.post(&url).json(&request))?; @@ -242,7 +240,7 @@ impl MastodonClient for Mastodon { } /// PUT /api/v1/filters/:id - fn update_filter(&self, id: &str, request: &mut AddFilterRequest) -> Result { + pub fn update_filter(&self, id: &str, request: &mut AddFilterRequest) -> Result { let url = self.route(&format!("/api/v1/filters/{}", id)); let response = self.send(self.client.put(&url).json(&request))?; @@ -257,7 +255,8 @@ impl MastodonClient for Mastodon { deserialise(response) } - fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result { + /// PATCH /api/v1/accounts/update_credentials + pub fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result { let changes = builder.build()?; let url = self.route("/api/v1/accounts/update_credentials"); let response = self.send(self.client.patch(&url).json(&changes))?; @@ -274,7 +273,7 @@ impl MastodonClient for Mastodon { } /// Post a new status to the account. - fn new_status(&self, status: NewStatus) -> Result { + pub fn new_status(&self, status: NewStatus) -> Result { let response = self.send(self.client.post(&self.route("/api/v1/statuses")).json(&status))?; deserialise(response) @@ -282,7 +281,7 @@ impl MastodonClient for Mastodon { /// Get timeline filtered by a hashtag(eg. `#coffee`) either locally or /// federated. - fn get_hashtag_timeline(&self, hashtag: &str, local: bool) -> Result> { + pub fn get_hashtag_timeline(&self, hashtag: &str, local: bool) -> Result> { let base = "/api/v1/timelines/tag/"; let url = if local { self.route(&format!("{}{}?local=1", base, hashtag)) @@ -295,49 +294,9 @@ impl MastodonClient for Mastodon { /// Get statuses of a single account by id. Optionally only with pictures /// and or excluding replies. - /// - /// # Example - /// - /// ```no_run - /// # extern crate elefren; - /// # use elefren::prelude::*; - /// # use std::error::Error; - /// # fn main() -> Result<(), Box> { - /// # let data = Data { - /// # base: "".into(), - /// # client_id: "".into(), - /// # client_secret: "".into(), - /// # redirect: "".into(), - /// # token: "".into(), - /// # }; - /// let client = Mastodon::from(data); - /// let statuses = client.statuses("user-id", None)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// ```no_run - /// # extern crate elefren; - /// # use elefren::prelude::*; - /// # use std::error::Error; - /// # fn main() -> Result<(), Box> { - /// # let data = Data { - /// # base: "".into(), - /// # client_id: "".into(), - /// # client_secret: "".into(), - /// # redirect: "".into(), - /// # token: "".into(), - /// # }; - /// let client = Mastodon::from(data); - /// let mut request = StatusesRequest::new(); - /// request.only_media(); - /// let statuses = client.statuses("user-id", request)?; - /// # Ok(()) - /// # } - /// ``` - fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result> - where - S: Into>>, + pub fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result> + where + S: Into>>, { let mut url = format!("{}/api/v1/accounts/{}/statuses", self.base, id); @@ -352,7 +311,7 @@ impl MastodonClient for Mastodon { /// Returns the client account's relationship to a list of other accounts. /// Such as whether they follow them or vice versa. - fn relationships(&self, ids: &[&str]) -> Result> { + pub fn relationships(&self, ids: &[&str]) -> Result> { let mut url = self.route("/api/v1/accounts/relationships?"); if ids.len() == 1 { @@ -373,7 +332,7 @@ impl MastodonClient for Mastodon { } /// Add a push notifications subscription - fn add_push_subscription(&self, request: &AddPushRequest) -> Result { + pub fn add_push_subscription(&self, request: &AddPushRequest) -> Result { let request = request.build()?; let response = self.send(self.client.post(&self.route("/api/v1/push/subscription")).json(&request))?; @@ -382,7 +341,7 @@ impl MastodonClient for Mastodon { /// Update the `data` portion of the push subscription associated with this /// access token - fn update_push_data(&self, request: &UpdatePushRequest) -> Result { + pub fn update_push_data(&self, request: &UpdatePushRequest) -> Result { let request = request.build(); let response = self.send(self.client.put(&self.route("/api/v1/push/subscription")).json(&request))?; @@ -390,48 +349,20 @@ impl MastodonClient for Mastodon { } /// Get all accounts that follow the authenticated user - fn follows_me(&self) -> Result> { + pub fn follows_me(&self) -> Result> { let me = self.verify_credentials()?; self.followers(&me.id) } /// Get all accounts that the authenticated user follows - fn followed_by_me(&self) -> Result> { + pub fn followed_by_me(&self) -> Result> { let me = self.verify_credentials()?; self.following(&me.id) } /// returns events that are relevant to the authorized user, i.e. home /// timeline & notifications - /// - /// # Example - /// - /// ```no_run - /// # extern crate elefren; - /// # use elefren::prelude::*; - /// # use std::error::Error; - /// use elefren::entities::event::Event; - /// # fn main() -> Result<(), Box> { - /// # let data = Data { - /// # base: "".into(), - /// # client_id: "".into(), - /// # client_secret: "".into(), - /// # redirect: "".into(), - /// # token: "".into(), - /// # }; - /// 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 => { /* .. */ }, - /// } - /// } - /// # Ok(()) - /// # } - /// ``` - fn streaming_user(&self) -> Result { + pub fn streaming_user(&self) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -451,7 +382,7 @@ impl MastodonClient for Mastodon { } /// returns all public statuses - fn streaming_public(&self) -> Result { + pub fn streaming_public(&self) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -471,7 +402,7 @@ impl MastodonClient for Mastodon { } /// Returns all local statuses - fn streaming_local(&self) -> Result { + pub fn streaming_local(&self) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -491,7 +422,7 @@ impl MastodonClient for Mastodon { } /// Returns all public statuses for a particular hashtag - fn streaming_public_hashtag(&self, hashtag: &str) -> Result { + pub fn streaming_public_hashtag(&self, hashtag: &str) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -512,7 +443,7 @@ impl MastodonClient for Mastodon { } /// Returns all local statuses for a particular hashtag - fn streaming_local_hashtag(&self, hashtag: &str) -> Result { + pub fn streaming_local_hashtag(&self, hashtag: &str) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -533,7 +464,7 @@ impl MastodonClient for Mastodon { } /// Returns statuses for a list - fn streaming_list(&self, list_id: &str) -> Result { + pub fn streaming_list(&self, list_id: &str) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -554,7 +485,7 @@ impl MastodonClient for Mastodon { } /// Returns all direct messages - fn streaming_direct(&self) -> Result { + pub fn streaming_direct(&self) -> Result { let mut url: url::Url = self.route("/api/v1/streaming").parse()?; url.query_pairs_mut() .append_pair("access_token", &self.token) @@ -613,7 +544,7 @@ impl MastodonClient for Mastodon { /// ## Errors /// This function may return an `Error::Http` before sending anything over the network if the /// `MediaBuilder` was supplied with a reader and a `mimetype` string which cannot be pasrsed. - fn media(&self, media: MediaBuilder) -> Result { + pub fn media(&self, media: MediaBuilder) -> Result { use media_builder::MediaBuilderData; let mut form = multipart::Form::new(); @@ -676,6 +607,7 @@ impl EventStream for WebSocket { #[derive(Debug)] /// Iterator that produces events from a mastodon streaming API event stream pub struct EventReader(R); + impl Iterator for EventReader { type Item = Event; @@ -683,15 +615,19 @@ impl Iterator for EventReader { let mut lines = Vec::new(); loop { if let Ok(line) = self.0.read_message() { + debug!("WS rx: {}", line); let line = line.trim().to_string(); if line.starts_with(':') || line.is_empty() { + debug!("discard as comment"); continue; } lines.push(line); if let Ok(event) = self.make_event(&lines) { + debug!("Parsed event"); lines.clear(); return Some(event); } else { + debug!("Failed to parse"); continue; } } @@ -704,12 +640,14 @@ impl EventReader { let event; let data; if let Some(event_line) = lines.iter().find(|line| line.starts_with("event:")) { + debug!("plaintext formatted event"); event = event_line[6..].trim().to_string(); data = lines .iter() .find(|line| line.starts_with("data:")) .map(|x| x[5..].trim().to_string()); } else { + debug!("JSON formatted event"); use serde::Deserialize; #[derive(Deserialize)] struct Message { @@ -834,11 +772,9 @@ impl MastodonUnauth { Ok(EventReader(WebSocket(client))) } -} -impl MastodonUnauthenticated for MastodonUnauth { /// GET /api/v1/statuses/:id - fn get_status(&self, id: &str) -> Result { + pub fn get_status(&self, id: &str) -> Result { let route = self.route("/api/v1/statuses")?; let route = route.join(id)?; let response = self.send(self.client.get(route))?; @@ -846,7 +782,7 @@ impl MastodonUnauthenticated for MastodonUnauth { } /// GET /api/v1/statuses/:id/context - fn get_context(&self, id: &str) -> Result { + pub fn get_context(&self, id: &str) -> Result { let route = self.route("/api/v1/statuses")?; let route = route.join(id)?; let route = route.join("context")?; @@ -855,7 +791,7 @@ impl MastodonUnauthenticated for MastodonUnauth { } /// GET /api/v1/statuses/:id/card - fn get_card(&self, id: &str) -> Result { + pub fn get_card(&self, id: &str) -> Result { let route = self.route("/api/v1/statuses")?; let route = route.join(id)?; let route = route.join("card")?; diff --git a/src/macros.rs b/src/macros.rs index 9e07e61..a002bba 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -14,33 +14,15 @@ macro_rules! methods { }; } -macro_rules! paged_routes { - - (($method:ident) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { +macro_rules! route_v1_paged { + (($method:ident) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `", stringify!($method), " /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set.", - "\n", - "```no_run", - "# extern crate elefren;\n", - "# use elefren::prelude::*;\n", - "# fn main() -> Result<(), Box<::std::error::Error>> {\n", - "# let data = Data {\n", - "# base: \"https://example.com\".into(),\n", - "# client_id: \"taosuah\".into(),\n", - "# client_secret: \"htnjdiuae\".into(),\n", - "# redirect: \"https://example.com\".into(),\n", - "# token: \"tsaohueaheis\".into(),\n", - "# };\n", - "let client = Mastodon::from(data);\n", - "client.", stringify!($name), "();\n", - "# Ok(())\n", - "# }\n", - "```" + "Equivalent to `", stringify!($method), " /api/v1/", $url, "`\n", + "# Errors\n", + "If `access_token` is not set.", ), - fn $name(&self) -> Result> { + pub fn $name(&self) -> Result> { let url = self.route(concat!("/api/v1/", $url)); let response = self.send( self.client.$method(&url) @@ -48,20 +30,17 @@ macro_rules! paged_routes { Page::new(self, response) } - } - - paged_routes!{$($rest)*} }; - ((get ($($(#[$m:meta])* $param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { + ((get ($($(#[$m:meta])* $param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `get /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set." + "Equivalent to `get /api/v1/", $url, "` with query params\n", + "# Errors\n", + "If `access_token` is not set.", ), - fn $name<'a>(&self, $($param: $typ,)*) -> Result> { + pub fn $name<'a>(&self, $($param: $typ,)*) -> Result> { use serde_urlencoded; use serde::Serialize; @@ -95,22 +74,18 @@ macro_rules! paged_routes { Page::new(self, response) } } - - paged_routes!{$($rest)*} }; - - () => {} } macro_rules! route_v2 { - ((get ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { + ((get ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `get /api/v2/", - $url, - "`\n# Errors\nIf `access_token` is not set." + "Equivalent to `get /api/v2/",$url,"`\n", + "# Errors\n", + "If `access_token` is not set." ), - fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> { + pub fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> { use serde_urlencoded; use serde::Serialize; @@ -137,23 +112,18 @@ macro_rules! route_v2 { self.get(self.route(&url)) } } - - route_v2!{$($rest)*} }; - - () => {} } -macro_rules! route { - - ((get ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { +macro_rules! route_v1 { + ((get ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `get /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set." + "Equivalent to `get /api/v1/", $url, "`\n", + "# Errors\n", + "If `access_token` is not set." ), - fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> { + pub fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> { use serde_urlencoded; use serde::Serialize; @@ -180,18 +150,16 @@ macro_rules! route { self.get(self.route(&url)) } } - - route!{$($rest)*} }; - (($method:ident ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { + (($method:ident ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `", stringify!($method), " /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set.", + "Equivalent to `", stringify!($method), " /api/v1/", $url, "`\n", + "# Errors\n", + "If `access_token` is not set." ), - fn $name(&self, $($param: $typ,)*) -> Result<$ret> { + pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> { let form_data = serde_json::json!({ $( @@ -215,107 +183,45 @@ macro_rules! route { deserialise(response) } } - - route!{$($rest)*} }; - (($method:ident) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { + (($method:ident) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `", stringify!($method), " /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set.", - "\n", - "```no_run", - "# extern crate elefren;\n", - "# use elefren::prelude::*;\n", - "# fn main() -> Result<(), Box<::std::error::Error>> {\n", - "# let data = Data {\n", - "# base: \"https://example.com\".into(),\n", - "# client_id: \"taosuah\".into(),\n", - "# client_secret: \"htnjdiuae\".into(),\n", - "# redirect: \"https://example.com\".into(),\n", - "# token: \"tsaohueaheis\".into(),\n", - "# };\n", - "let client = Mastodon::from(data);\n", - "client.", stringify!($name), "();\n", - "# Ok(())\n", - "# }\n", - "```" + "Equivalent to `", stringify!($method), " /api/v1/", $url, "`\n", + "# Errors\n", + "If `access_token` is not set." ), - fn $name(&self) -> Result<$ret> { + pub fn $name(&self) -> Result<$ret> { self.$method(self.route(concat!("/api/v1/", $url))) } } - - route!{$($rest)*} }; - - () => {} } -macro_rules! route_id { - - ($(($method:ident) $name:ident: $url:expr => $ret:ty,)*) => { - $( - doc_comment::doc_comment! { - concat!( - "Equivalent to `", stringify!($method), " /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set.", - "\n", - "```no_run", - "# extern crate elefren;\n", - "# use elefren::prelude::*;\n", - "# fn main() -> Result<(), Box<::std::error::Error>> {\n", - "# let data = Data {\n", - "# base: \"https://example.com\".into(),\n", - "# client_id: \"taosuah\".into(),\n", - "# client_secret: \"htnjdiuae\".into(),\n", - "# redirect: \"https://example.com\".into(),\n", - "# token: \"tsaohueaheis\".into(),\n", - "# };\n", - "let client = Mastodon::from(data);\n", - "client.", stringify!($name), "(\"42\");\n", - "# Ok(())\n", - "# }\n", - "```" - ), - fn $name(&self, id: &str) -> Result<$ret> { - self.$method(self.route(&format!(concat!("/api/v1/", $url), id))) - } +macro_rules! route_v1_id { + (($method:ident) $name:ident: $url:expr => $ret:ty) => { + doc_comment::doc_comment! { + concat!( + "Equivalent to `", stringify!($method), " /api/v1/", $url, "`\n", + "# Errors\n", + "If `access_token` is not set." + ), + pub fn $name(&self, id: &str) -> Result<$ret> { + self.$method(self.route(&format!(concat!("/api/v1/", $url), id))) } - )* + } } - } -macro_rules! paged_routes_with_id { - - (($method:ident) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => { +macro_rules! route_v1_paged_id { + (($method:ident) $name:ident: $url:expr => $ret:ty) => { doc_comment::doc_comment! { concat!( - "Equivalent to `", stringify!($method), " /api/v1/", - $url, - "`\n# Errors\nIf `access_token` is not set.", - "\n", - "```no_run", - "# extern crate elefren;\n", - "# use elefren::prelude::*;\n", - "# fn main() -> Result<(), Box<::std::error::Error>> {\n", - "# let data = Data {\n", - "# base: \"https://example.com\".into(),\n", - "# client_id: \"taosuah\".into(),\n", - "# client_secret: \"htnjdiuae\".into(),\n", - "# redirect: \"https://example.com\".into(),\n", - "# token: \"tsaohueaheis\".into(),\n", - "# };\n", - "let client = Mastodon::from(data);\n", - "client.", stringify!($name), "(\"some-id\");\n", - "# Ok(())\n", - "# }\n", - "```" + "Equivalent to `", stringify!($method), " /api/v1/", $url, "` with ID\n", + "# Errors\n", + "If `access_token` is not set." ), - fn $name(&self, id: &str) -> Result> { + pub fn $name(&self, id: &str) -> Result> { let url = self.route(&format!(concat!("/api/v1/", $url), id)); let response = self.send( self.client.$method(&url) @@ -324,9 +230,5 @@ macro_rules! paged_routes_with_id { Page::new(self, response) } } - - paged_routes_with_id!{$($rest)*} }; - - () => {} }