removed the trait so it can be easier made async

master
Ondřej Hruška 3 years ago
parent 3c7a84a124
commit b6b7372ebb
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 294
      src/lib.rs
  2. 196
      src/macros.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<WebSocket>;
/// 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<Data> for Mastodon {
}
}
impl MastodonClient for Mastodon {
type Stream = EventReader<WebSocket>;
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<u64>, 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<Filter>,
(get) get_follow_suggestions: "suggestions" => Vec<Account>,
}
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<Filter> {
type MastodonStream = EventReader<WebSocket>;
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<u64>, 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<Filter>);
route_v1!((get) get_follow_suggestions: "suggestions" => Vec<Account>);
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<Filter> {
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<Filter> {
pub fn update_filter(&self, id: &str, request: &mut AddFilterRequest) -> Result<Filter> {
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<Account> {
/// PATCH /api/v1/accounts/update_credentials
pub fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> {
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<Status> {
pub fn new_status(&self, status: NewStatus) -> Result<Status> {
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<Page<Status>> {
pub fn get_hashtag_timeline(&self, hashtag: &str, local: bool) -> Result<Page<Status>> {
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<dyn Error>> {
/// # 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<dyn Error>> {
/// # 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<Page<Status>>
where
S: Into<Option<StatusesRequest<'a>>>,
pub fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status>>
where
S: Into<Option<StatusesRequest<'a>>>,
{
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<Page<Relationship>> {
pub fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship>> {
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<Subscription> {
pub fn add_push_subscription(&self, request: &AddPushRequest) -> Result<Subscription> {
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<Subscription> {
pub fn update_push_data(&self, request: &UpdatePushRequest) -> Result<Subscription> {
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<Page<Account>> {
pub fn follows_me(&self) -> Result<Page<Account>> {
let me = self.verify_credentials()?;
self.followers(&me.id)
}
/// Get all accounts that the authenticated user follows
fn followed_by_me(&self) -> Result<Page<Account>> {
pub fn followed_by_me(&self) -> Result<Page<Account>> {
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<dyn Error>> {
/// # 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<Self::Stream> {
pub fn streaming_user(&self) -> Result<MastodonStream> {
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<Self::Stream> {
pub fn streaming_public(&self) -> Result<MastodonStream> {
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<Self::Stream> {
pub fn streaming_local(&self) -> Result<MastodonStream> {
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<Self::Stream> {
pub fn streaming_public_hashtag(&self, hashtag: &str) -> Result<MastodonStream> {
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<Self::Stream> {
pub fn streaming_local_hashtag(&self, hashtag: &str) -> Result<MastodonStream> {
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<Self::Stream> {
pub fn streaming_list(&self, list_id: &str) -> Result<MastodonStream> {
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<Self::Stream> {
pub fn streaming_direct(&self) -> Result<MastodonStream> {
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<Attachment> {
pub fn media(&self, media: MediaBuilder) -> Result<Attachment> {
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: EventStream>(R);
impl<R: EventStream> Iterator for EventReader<R> {
type Item = Event;
@ -683,15 +615,19 @@ impl<R: EventStream> Iterator for EventReader<R> {
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<R: EventStream> EventReader<R> {
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<Status> {
pub fn get_status(&self, id: &str) -> Result<Status> {
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<Context> {
pub fn get_context(&self, id: &str) -> Result<Context> {
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<Card> {
pub fn get_card(&self, id: &str) -> Result<Card> {
let route = self.route("/api/v1/statuses")?;
let route = route.join(id)?;
let route = route.join("card")?;

@ -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<Page<$ret>> {
pub fn $name(&self) -> Result<Page<$ret>> {
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<Page<$ret>> {
pub fn $name<'a>(&self, $($param: $typ,)*) -> Result<Page<$ret>> {
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<Page<$ret>> {
pub fn $name(&self, id: &str) -> Result<Page<$ret>> {
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)*}
};
() => {}
}

Loading…
Cancel
Save