forked from MightyPork/elefren-fork
all asyncified except for streaming
This commit is contained in:
+9
-25
@@ -12,36 +12,20 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
doc-comment = "0.3"
|
||||
envy = { version = "0.4.0", optional = true }
|
||||
hyper-old-types = "0.11.0"
|
||||
#envy = { version = "0.4.0", optional = true }
|
||||
hyper-old-types = "0.11.0" # Used to parse the link header
|
||||
isolang = { version = "1.0", features = ["serde_serialize"] }
|
||||
log = "^0.4"
|
||||
reqwest = { version = "0.11.4", default-features = false, features = ["json", "blocking", "multipart"] }
|
||||
reqwest = { version = "0.11.4", default-features = false, features = ["json", "blocking", "multipart", "rustls-tls", "stream"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_urlencoded = "0.6.1"
|
||||
serde_qs = "0.8.4"
|
||||
url = "2.1.1"
|
||||
tap-reader = "1"
|
||||
#tap-reader = "1"
|
||||
toml = { version = "^0.5.0", optional = true }
|
||||
tungstenite = "0.15.0"
|
||||
|
||||
[dependencies.chrono]
|
||||
version = "0.4"
|
||||
features = ["serde"]
|
||||
|
||||
[features]
|
||||
default = ["reqwest/default-tls","tungstenite/native-tls"]
|
||||
json = []
|
||||
env = ["envy"]
|
||||
all = ["toml", "json", "env"]
|
||||
rustls-tls = ["reqwest/rustls-tls","tungstenite/__rustls-tls"]
|
||||
nightly = []
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0.3"
|
||||
indoc = "1.0.2"
|
||||
pretty_env_logger = "0.4.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["all"]
|
||||
tokio-tungstenite = { version = "0.15.0", features = ["rustls-tls"] }
|
||||
futures = { version = "0.3" }
|
||||
tokio = {version = "1", features = ["full"] }
|
||||
tokio-util = { version = "0.6", features = [ "compat", "io" ] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
@@ -24,7 +24,7 @@ use serde::Deserialize;
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ItemsIter<'a, T: Clone + for<'de> Deserialize<'de>> {
|
||||
pub struct ItemsIter<'a, T: Clone + for<'de> Deserialize<'de>> {
|
||||
page: Page<'a, T>,
|
||||
buffer: Vec<T>,
|
||||
cur_idx: usize,
|
||||
@@ -45,8 +45,8 @@ impl<'a, T: Clone + for<'de> Deserialize<'de>> ItemsIter<'a, T> {
|
||||
self.buffer.is_empty() || self.cur_idx == self.buffer.len()
|
||||
}
|
||||
|
||||
fn fill_next_page(&mut self) -> Option<()> {
|
||||
let items = if let Ok(items) = self.page.next_page() {
|
||||
async fn fill_next_page(&mut self) -> Option<()> {
|
||||
let items = if let Ok(items) = self.page.next_page().await {
|
||||
items
|
||||
} else {
|
||||
return None;
|
||||
@@ -62,12 +62,8 @@ impl<'a, T: Clone + for<'de> Deserialize<'de>> ItemsIter<'a, T> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone + for<'de> Deserialize<'de>> Iterator for ItemsIter<'a, T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
pub async fn next_item(&mut self) -> Option<T> {
|
||||
if self.use_initial {
|
||||
if self.page.initial_items.is_empty() || self.cur_idx == self.page.initial_items.len() {
|
||||
return None;
|
||||
@@ -81,7 +77,7 @@ impl<'a, T: Clone + for<'de> Deserialize<'de>> Iterator for ItemsIter<'a, T> {
|
||||
}
|
||||
Some(self.page.initial_items[idx].clone())
|
||||
} else {
|
||||
if self.need_next_page() && self.fill_next_page().is_none() {
|
||||
if self.need_next_page() && self.fill_next_page().await.is_none() {
|
||||
return None;
|
||||
}
|
||||
let idx = self.cur_idx;
|
||||
|
||||
+8
-8
@@ -12,7 +12,7 @@ use reqwest::{header::ToStrError as HeaderStrError, Error as HttpError, StatusCo
|
||||
use serde_json::Error as SerdeError;
|
||||
use serde_qs::Error as SerdeQsError;
|
||||
use serde_urlencoded::ser::Error as UrlEncodedError;
|
||||
use tungstenite::error::Error as WebSocketError;
|
||||
// use tungstenite::error::Error as WebSocketError;
|
||||
use url::ParseError as UrlError;
|
||||
|
||||
/// Convience type over `std::result::Result` with `Error` as the error type.
|
||||
@@ -57,14 +57,14 @@ pub enum Error {
|
||||
HeaderStrError(HeaderStrError),
|
||||
/// Error parsing the http Link header
|
||||
HeaderParseError(HeaderParseError),
|
||||
#[cfg(feature = "env")]
|
||||
/// Error deserializing from the environment
|
||||
Envy(EnvyError),
|
||||
// #[cfg(feature = "env")]
|
||||
// /// Error deserializing from the environment
|
||||
// Envy(EnvyError),
|
||||
/// Error serializing to a query string
|
||||
SerdeQs(SerdeQsError),
|
||||
/// WebSocket error
|
||||
WebSocket(WebSocketError),
|
||||
/// Other errors
|
||||
// WebSocket(WebSocketError),
|
||||
// /// Other errors
|
||||
Other(String),
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ impl error::Error for Error {
|
||||
#[cfg(feature = "env")]
|
||||
Error::Envy(ref e) => e,
|
||||
Error::SerdeQs(ref e) => e,
|
||||
Error::WebSocket(ref e) => e,
|
||||
// Error::WebSocket(ref e) => e,
|
||||
|
||||
Error::Client(..) | Error::Server(..) => return None,
|
||||
Error::ClientIdRequired => return None,
|
||||
@@ -148,7 +148,7 @@ from! {
|
||||
HeaderParseError, HeaderParseError,
|
||||
#[cfg(feature = "env")] EnvyError, Envy,
|
||||
SerdeQsError, SerdeQs,
|
||||
WebSocketError, WebSocket,
|
||||
// WebSocketError, WebSocket,
|
||||
String, Other,
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ use crate::{errors::Result, registration::Registered, Mastodon};
|
||||
|
||||
/// Finishes the authentication process for the given `Registered` object,
|
||||
/// using the command-line
|
||||
pub fn authenticate(registration: Registered) -> Result<Mastodon> {
|
||||
pub async fn authenticate(registration: Registered) -> Result<Mastodon> {
|
||||
let url = registration.authorize_url()?;
|
||||
|
||||
let stdout = io::stdout();
|
||||
@@ -20,5 +20,5 @@ pub fn authenticate(registration: Registered) -> Result<Mastodon> {
|
||||
let mut input = String::new();
|
||||
stdin.read_line(&mut input)?;
|
||||
let code = input.trim();
|
||||
registration.complete(code)
|
||||
registration.complete(code).await
|
||||
}
|
||||
|
||||
+101
-91
@@ -57,31 +57,30 @@
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![deny(
|
||||
missing_docs,
|
||||
warnings,
|
||||
missing_debug_implementations,
|
||||
missing_copy_implementations,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unsafe_code,
|
||||
unstable_features,
|
||||
unused_import_braces,
|
||||
unused_qualifications
|
||||
)]
|
||||
// #![deny(
|
||||
// missing_docs,
|
||||
// warnings,
|
||||
// missing_debug_implementations,
|
||||
// missing_copy_implementations,
|
||||
// trivial_casts,
|
||||
// trivial_numeric_casts,
|
||||
// unsafe_code,
|
||||
// unstable_features,
|
||||
// unused_import_braces,
|
||||
// unused_qualifications
|
||||
// )]
|
||||
// #![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 std::{borrow::Cow, ops};
|
||||
|
||||
pub use isolang::Language;
|
||||
use reqwest::blocking::{Client, multipart, RequestBuilder, Response};
|
||||
use tap_reader::Tap;
|
||||
use tungstenite::stream::MaybeTlsStream;
|
||||
use reqwest::{Client, multipart, RequestBuilder, Response, Body};
|
||||
// use tap_reader::Tap;
|
||||
// use tungstenite::stream::MaybeTlsStream;
|
||||
|
||||
use crate::{entities::prelude::*, page::Page};
|
||||
pub use crate::{
|
||||
@@ -104,7 +103,6 @@ pub mod entities;
|
||||
pub mod errors;
|
||||
/// Collection of helpers for serializing/deserializing `Data` objects
|
||||
pub mod helpers;
|
||||
mod mastodon_client;
|
||||
/// Constructing media attachments for a status.
|
||||
pub mod media_builder;
|
||||
/// Handling multiple pages of entities.
|
||||
@@ -132,26 +130,11 @@ pub mod prelude {
|
||||
/// 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 {
|
||||
format!("{}{}", self.base, url)
|
||||
}
|
||||
|
||||
pub(crate) fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
let request = req.bearer_auth(&self.token).build()?;
|
||||
Ok(self.client.execute(request)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Data> for Mastodon {
|
||||
/// Creates a mastodon instance from the data struct.
|
||||
fn from(data: Data) -> Mastodon {
|
||||
@@ -161,7 +144,20 @@ impl From<Data> for Mastodon {
|
||||
}
|
||||
}
|
||||
|
||||
type MastodonStream = EventReader<WebSocket>;
|
||||
// type MastodonStream = EventReader<WebSocket>;
|
||||
|
||||
impl Mastodon {
|
||||
methods![get, post, delete,];
|
||||
|
||||
fn route(&self, url: &str) -> String {
|
||||
format!("{}{}", self.base, url)
|
||||
}
|
||||
|
||||
pub(crate) async fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
let request = req.bearer_auth(&self.token).build()?;
|
||||
Ok(self.client.execute(request).await?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mastodon {
|
||||
route_v1_paged!((get) favourites: "favourites" => Status);
|
||||
@@ -224,9 +220,9 @@ impl Mastodon {
|
||||
route_v1_id!((post) unendorse_user: "accounts/{}/unpin" => Relationship);
|
||||
|
||||
/// POST /api/v1/filters
|
||||
pub fn add_filter(&self, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||
pub async 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))?;
|
||||
let response = self.send(self.client.post(&url).json(&request)).await?;
|
||||
|
||||
let status = response.status();
|
||||
|
||||
@@ -236,13 +232,13 @@ impl Mastodon {
|
||||
return Err(Error::Server(status));
|
||||
}
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// PUT /api/v1/filters/:id
|
||||
pub fn update_filter(&self, id: &str, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||
pub async 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))?;
|
||||
let response = self.send(self.client.put(&url).json(&request)).await?;
|
||||
|
||||
let status = response.status();
|
||||
|
||||
@@ -252,14 +248,14 @@ impl Mastodon {
|
||||
return Err(Error::Server(status));
|
||||
}
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// PATCH /api/v1/accounts/update_credentials
|
||||
pub fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> {
|
||||
pub async 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))?;
|
||||
let response = self.send(self.client.patch(&url).json(&changes)).await?;
|
||||
|
||||
let status = response.status();
|
||||
|
||||
@@ -269,19 +265,19 @@ impl Mastodon {
|
||||
return Err(Error::Server(status));
|
||||
}
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// Post a new status to the account.
|
||||
pub fn new_status(&self, status: NewStatus) -> Result<Status> {
|
||||
let response = self.send(self.client.post(&self.route("/api/v1/statuses")).json(&status))?;
|
||||
pub async fn new_status(&self, status: NewStatus) -> Result<Status> {
|
||||
let response = self.send(self.client.post(&self.route("/api/v1/statuses")).json(&status)).await?;
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// Get timeline filtered by a hashtag(eg. `#coffee`) either locally or
|
||||
/// federated.
|
||||
pub fn get_hashtag_timeline(&self, hashtag: &str, local: bool) -> Result<Page<Status>> {
|
||||
pub async fn get_hashtag_timeline<'a>(&'a self, hashtag: &str, local: bool) -> Result<Page<'a, Status>> {
|
||||
let base = "/api/v1/timelines/tag/";
|
||||
let url = if local {
|
||||
self.route(&format!("{}{}?local=1", base, hashtag))
|
||||
@@ -289,12 +285,12 @@ impl Mastodon {
|
||||
self.route(&format!("{}{}", base, hashtag))
|
||||
};
|
||||
|
||||
Page::new(self, self.send(self.client.get(&url))?)
|
||||
Page::new(self, self.send(self.client.get(&url)).await?).await
|
||||
}
|
||||
|
||||
/// Get statuses of a single account by id. Optionally only with pictures
|
||||
/// and or excluding replies.
|
||||
pub fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status>>
|
||||
pub async fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<'b, Status>>
|
||||
where
|
||||
S: Into<Option<StatusesRequest<'a>>>,
|
||||
{
|
||||
@@ -304,14 +300,14 @@ impl Mastodon {
|
||||
url = format!("{}{}", url, request.to_querystring()?);
|
||||
}
|
||||
|
||||
let response = self.send(self.client.get(&url))?;
|
||||
let response = self.send(self.client.get(&url)).await?;
|
||||
|
||||
Page::new(self, response)
|
||||
Page::new(self, response).await
|
||||
}
|
||||
|
||||
/// Returns the client account's relationship to a list of other accounts.
|
||||
/// Such as whether they follow them or vice versa.
|
||||
pub fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship>> {
|
||||
pub async fn relationships<'a>(&'a self, ids: &[&str]) -> Result<Page<'a, Relationship>> {
|
||||
let mut url = self.route("/api/v1/accounts/relationships?");
|
||||
|
||||
if ids.len() == 1 {
|
||||
@@ -326,40 +322,42 @@ impl Mastodon {
|
||||
url.pop();
|
||||
}
|
||||
|
||||
let response = self.send(self.client.get(&url))?;
|
||||
let response = self.send(self.client.get(&url)).await?;
|
||||
|
||||
Page::new(self, response)
|
||||
Page::new(self, response).await
|
||||
}
|
||||
|
||||
/// Add a push notifications subscription
|
||||
pub fn add_push_subscription(&self, request: &AddPushRequest) -> Result<Subscription> {
|
||||
pub async 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))?;
|
||||
let response = self.send(self.client.post(&self.route("/api/v1/push/subscription")).json(&request)).await?;
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// Update the `data` portion of the push subscription associated with this
|
||||
/// access token
|
||||
pub fn update_push_data(&self, request: &UpdatePushRequest) -> Result<Subscription> {
|
||||
pub async 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))?;
|
||||
let response = self.send(self.client.put(&self.route("/api/v1/push/subscription")).json(&request)).await?;
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// Get all accounts that follow the authenticated user
|
||||
pub fn follows_me(&self) -> Result<Page<Account>> {
|
||||
let me = self.verify_credentials()?;
|
||||
self.followers(&me.id)
|
||||
pub async fn follows_me<'a>(&'a self) -> Result<Page<'a, Account>> {
|
||||
let me = self.verify_credentials().await?;
|
||||
self.followers(&me.id).await
|
||||
}
|
||||
|
||||
/// Get all accounts that the authenticated user follows
|
||||
pub fn followed_by_me(&self) -> Result<Page<Account>> {
|
||||
let me = self.verify_credentials()?;
|
||||
self.following(&me.id)
|
||||
pub async fn followed_by_me<'a>(&'a self) -> Result<Page<'a, Account>> {
|
||||
let me = self.verify_credentials().await?;
|
||||
self.following(&me.id).await
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/// returns events that are relevant to the authorized user, i.e. home
|
||||
/// timeline & notifications
|
||||
pub fn streaming_user(&self) -> Result<MastodonStream> {
|
||||
@@ -367,7 +365,7 @@ impl Mastodon {
|
||||
url.query_pairs_mut()
|
||||
.append_pair("access_token", &self.token)
|
||||
.append_pair("stream", "user");
|
||||
let mut url: url::Url = reqwest::blocking::get(url.as_str())?.url().as_str().parse()?;
|
||||
let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?;
|
||||
let new_scheme = match url.scheme() {
|
||||
"http" => "ws",
|
||||
"https" => "wss",
|
||||
@@ -503,6 +501,7 @@ impl Mastodon {
|
||||
|
||||
Ok(EventReader(WebSocket(client)))
|
||||
}
|
||||
*/
|
||||
|
||||
/// Upload some media to the server for possible attaching to a new status
|
||||
///
|
||||
@@ -544,14 +543,13 @@ impl 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.
|
||||
pub fn media(&self, media: MediaBuilder) -> Result<Attachment> {
|
||||
pub async fn media(&self, media: MediaBuilder) -> Result<Attachment> {
|
||||
use media_builder::MediaBuilderData;
|
||||
|
||||
let mut form = multipart::Form::new();
|
||||
form = match media.data {
|
||||
MediaBuilderData::Reader(reader) => {
|
||||
let mut part = multipart::Part::reader(reader);
|
||||
|
||||
let mut part = multipart::Part::stream(Body::wrap_stream(tokio_util::io::ReaderStream::new(reader)));
|
||||
if let Some(filename) = media.filename {
|
||||
part = part.file_name(filename);
|
||||
}
|
||||
@@ -562,7 +560,12 @@ impl Mastodon {
|
||||
|
||||
form.part("file", part)
|
||||
}
|
||||
MediaBuilderData::File(file) => form.file("file", &file)?,
|
||||
MediaBuilderData::File(file) => {
|
||||
let f = tokio::fs::OpenOptions::new().read(true).open(file).await?;
|
||||
let part = multipart::Part::stream(Body::wrap_stream(tokio_util::io::ReaderStream::new(f)));
|
||||
form.part("file", part)
|
||||
// form.file("file", &file)?
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(description) = media.description {
|
||||
@@ -573,12 +576,14 @@ impl Mastodon {
|
||||
form = form.text("focus", format!("{},{}", x, y));
|
||||
}
|
||||
|
||||
let response = self.send(self.client.post(&self.route("/api/v1/media")).multipart(form))?;
|
||||
let response = self.send(self.client.post(&self.route("/api/v1/media")).multipart(form)).await?;
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
#[derive(Debug)]
|
||||
/// WebSocket newtype so that EventStream can be implemented without coherency
|
||||
/// issues
|
||||
@@ -680,6 +685,8 @@ impl<R: EventStream> EventReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
impl ops::Deref for Mastodon {
|
||||
type Target = Data;
|
||||
|
||||
@@ -750,11 +757,13 @@ impl MastodonUnauth {
|
||||
Ok(self.base.join(url)?)
|
||||
}
|
||||
|
||||
fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
async fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
let req = req.build()?;
|
||||
Ok(self.client.execute(req)?)
|
||||
Ok(self.client.execute(req).await?)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/// Get a stream of the public timeline
|
||||
pub fn streaming_public(&self) -> Result<EventReader<WebSocket>> {
|
||||
let mut url: url::Url = self.route("/api/v1/streaming/public/local")?;
|
||||
@@ -773,48 +782,49 @@ impl MastodonUnauth {
|
||||
Ok(EventReader(WebSocket(client)))
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/// GET /api/v1/statuses/:id
|
||||
pub fn get_status(&self, id: &str) -> Result<Status> {
|
||||
pub async 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))?;
|
||||
deserialise(response)
|
||||
let response = self.send(self.client.get(route)).await?;
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// GET /api/v1/statuses/:id/context
|
||||
pub fn get_context(&self, id: &str) -> Result<Context> {
|
||||
pub async 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")?;
|
||||
let response = self.send(self.client.get(route))?;
|
||||
deserialise(response)
|
||||
let response = self.send(self.client.get(route)).await?;
|
||||
deserialise_response(response).await
|
||||
}
|
||||
|
||||
/// GET /api/v1/statuses/:id/card
|
||||
pub fn get_card(&self, id: &str) -> Result<Card> {
|
||||
pub async 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")?;
|
||||
let response = self.send(self.client.get(route))?;
|
||||
deserialise(response)
|
||||
let response = self.send(self.client.get(route)).await?;
|
||||
deserialise_response(response).await
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the HTTP response body from JSON. Pass up deserialization errors
|
||||
// transparently.
|
||||
fn deserialise<T: for<'de> serde::Deserialize<'de>>(response: Response) -> Result<T> {
|
||||
let mut reader = Tap::new(response);
|
||||
|
||||
match serde_json::from_reader(&mut reader) {
|
||||
async fn deserialise_response<T: for<'de> serde::Deserialize<'de>>(response: Response) -> Result<T> {
|
||||
let bytes = response.bytes().await?;
|
||||
match serde_json::from_slice(&bytes) {
|
||||
Ok(t) => {
|
||||
log::debug!("{}", String::from_utf8_lossy(&reader.bytes));
|
||||
debug!("{}", String::from_utf8_lossy(&bytes));
|
||||
Ok(t)
|
||||
}
|
||||
// If deserializing into the desired type fails try again to
|
||||
// see if this is an error response.
|
||||
Err(e) => {
|
||||
log::error!("{}", String::from_utf8_lossy(&reader.bytes));
|
||||
if let Ok(error) = serde_json::from_slice(&reader.bytes) {
|
||||
error!("{}", String::from_utf8_lossy(&bytes));
|
||||
if let Ok(error) = serde_json::from_slice(&bytes) {
|
||||
return Err(Error::Api(error));
|
||||
}
|
||||
Err(e.into())
|
||||
|
||||
+23
-23
@@ -1,14 +1,14 @@
|
||||
macro_rules! methods {
|
||||
($($method:ident,)+) => {
|
||||
$(
|
||||
fn $method<T: for<'de> serde::Deserialize<'de>>(&self, url: String)
|
||||
async fn $method<T: for<'de> serde::Deserialize<'de>>(&self, url: String)
|
||||
-> Result<T>
|
||||
{
|
||||
let response = self.send(
|
||||
self.client.$method(&url)
|
||||
)?;
|
||||
).await?;
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
)+
|
||||
};
|
||||
@@ -22,13 +22,13 @@ macro_rules! route_v1_paged {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set.",
|
||||
),
|
||||
pub fn $name(&self) -> Result<Page<$ret>> {
|
||||
pub async fn $name<'a>(&'a self) -> Result<Page<'a, $ret>> {
|
||||
let url = self.route(concat!("/api/v1/", $url));
|
||||
let response = self.send(
|
||||
self.client.$method(&url)
|
||||
)?;
|
||||
).await?;
|
||||
|
||||
Page::new(self, response)
|
||||
Page::new(self, response).await
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -40,7 +40,7 @@ macro_rules! route_v1_paged {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set.",
|
||||
),
|
||||
pub fn $name<'a>(&self, $($param: $typ,)*) -> Result<Page<$ret>> {
|
||||
pub async fn $name<'a, 'b>(&'b self, $($param: $typ,)*) -> Result<Page<'b, $ret>> {
|
||||
use serde_urlencoded;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -69,9 +69,9 @@ macro_rules! route_v1_paged {
|
||||
|
||||
let response = self.send(
|
||||
self.client.get(&url)
|
||||
)?;
|
||||
).await?;
|
||||
|
||||
Page::new(self, response)
|
||||
Page::new(self, response).await
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -85,7 +85,7 @@ macro_rules! route_v2 {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set."
|
||||
),
|
||||
pub fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||
pub async fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||
use serde_urlencoded;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -109,7 +109,7 @@ macro_rules! route_v2 {
|
||||
|
||||
let url = format!(concat!("/api/v2/", $url, "?{}"), &qs);
|
||||
|
||||
self.get(self.route(&url))
|
||||
self.get(self.route(&url)).await
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -123,7 +123,7 @@ macro_rules! route_v1 {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set."
|
||||
),
|
||||
pub fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||
pub async fn $name<'a>(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||
use serde_urlencoded;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -147,7 +147,7 @@ macro_rules! route_v1 {
|
||||
|
||||
let url = format!(concat!("/api/v1/", $url, "?{}"), &qs);
|
||||
|
||||
self.get(self.route(&url))
|
||||
self.get(self.route(&url)).await
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -159,7 +159,7 @@ macro_rules! route_v1 {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set."
|
||||
),
|
||||
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||
pub async fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||
|
||||
let form_data = serde_json::json!({
|
||||
$(
|
||||
@@ -170,7 +170,7 @@ macro_rules! route_v1 {
|
||||
let response = self.send(
|
||||
self.client.$method(&self.route(concat!("/api/v1/", $url)))
|
||||
.json(&form_data)
|
||||
)?;
|
||||
).await?;
|
||||
|
||||
let status = response.status().clone();
|
||||
|
||||
@@ -180,7 +180,7 @@ macro_rules! route_v1 {
|
||||
return Err(Error::Server(status));
|
||||
}
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -192,8 +192,8 @@ macro_rules! route_v1 {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set."
|
||||
),
|
||||
pub fn $name(&self) -> Result<$ret> {
|
||||
self.$method(self.route(concat!("/api/v1/", $url)))
|
||||
pub async fn $name(&self) -> Result<$ret> {
|
||||
self.$method(self.route(concat!("/api/v1/", $url))).await
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -207,8 +207,8 @@ macro_rules! route_v1_id {
|
||||
"# 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)))
|
||||
pub async fn $name(&self, id: &str) -> Result<$ret> {
|
||||
self.$method(self.route(&format!(concat!("/api/v1/", $url), id))).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,13 +221,13 @@ macro_rules! route_v1_paged_id {
|
||||
"# Errors\n",
|
||||
"If `access_token` is not set."
|
||||
),
|
||||
pub fn $name(&self, id: &str) -> Result<Page<$ret>> {
|
||||
pub async fn $name<'a>(&'a self, id: &str) -> Result<Page<'a, $ret>> {
|
||||
let url = self.route(&format!(concat!("/api/v1/", $url), id));
|
||||
let response = self.send(
|
||||
self.client.$method(&url)
|
||||
)?;
|
||||
).await?;
|
||||
|
||||
Page::new(self, response)
|
||||
Page::new(self, response).await
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
entities::prelude::*,
|
||||
errors::Result,
|
||||
media_builder::MediaBuilder,
|
||||
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 {
|
||||
/// Type that wraps streaming API streams
|
||||
type Stream: Iterator<Item = Event>;
|
||||
|
||||
/// GET /api/v1/favourites
|
||||
fn favourites(&self) -> Result<Page<Status>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/blocks
|
||||
fn blocks(&self) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/domain_blocks
|
||||
fn domain_blocks(&self) -> Result<Page<String>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/follow_requests
|
||||
fn follow_requests(&self) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/timelines/home
|
||||
fn get_home_timeline(&self) -> Result<Page<Status>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/custom_emojis
|
||||
fn get_emojis(&self) -> Result<Page<Emoji>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/mutes
|
||||
fn mutes(&self) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/notifications
|
||||
fn notifications(&self) -> Result<Page<Notification>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/reports
|
||||
fn reports(&self) -> Result<Page<Report>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/accounts/:id/followers
|
||||
fn followers(&self, id: &str) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/accounts/:id/following
|
||||
fn following(&self, id: &str) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/statuses/:id/reblogged_by
|
||||
fn reblogged_by(&self, id: &str) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/statuses/:id/favourited_by
|
||||
fn favourited_by(&self, id: &str) -> Result<Page<Account>> {
|
||||
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(&self, q: &'_ str, resolve: bool) -> Result<SearchResult> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v2/search
|
||||
fn search_v2(&self, q: &'_ 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, media_builder: MediaBuilder) -> 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?local=true
|
||||
fn get_local_timeline(&self) -> Result<Page<Status>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/timelines/public?local=false
|
||||
fn get_federated_timeline(&self) -> Result<Page<Status>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/timelines/tag/:hashtag
|
||||
fn get_hashtag_timeline(&self, hashtag: &str, local: bool) -> Result<Page<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>>
|
||||
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>> {
|
||||
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>> {
|
||||
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>> {
|
||||
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
|
||||
/// # extern crate elefren;
|
||||
/// # use std::error::Error;
|
||||
/// # use elefren::prelude::*;
|
||||
/// # 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 follows_me = client.follows_me()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
fn follows_me(&self) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// Shortcut for
|
||||
/// `let me = client.verify_credentials(); client.following(&me.id)`
|
||||
///
|
||||
/// ```no_run
|
||||
/// # extern crate elefren;
|
||||
/// # use std::error::Error;
|
||||
/// # use elefren::prelude::*;
|
||||
/// # 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 follows_me = client.followed_by_me()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
fn followed_by_me(&self) -> Result<Page<Account>> {
|
||||
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 {
|
||||
/// 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>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
/// GET /api/v1/statuses/:id/favourited_by
|
||||
fn favourited_by(&self, id: &str) -> Result<Page<Account>> {
|
||||
unimplemented!("This method was not implemented");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::io::AsyncRead;
|
||||
use std::pin::Pin;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A builder pattern struct for preparing a single attachment for upload.
|
||||
@@ -28,7 +29,7 @@ pub struct MediaBuilder {
|
||||
/// Enum representing possible sources of attachments to upload
|
||||
pub enum MediaBuilderData {
|
||||
/// An arbitrary reader. It is useful for reading from media already in memory.
|
||||
Reader(Box<dyn Read + Send>),
|
||||
Reader(Pin<Box<dyn AsyncRead + Send + Sync>>),
|
||||
|
||||
/// Variant represening a file path of the file to attach.
|
||||
File(PathBuf),
|
||||
@@ -45,9 +46,9 @@ impl fmt::Debug for MediaBuilderData {
|
||||
|
||||
impl MediaBuilder {
|
||||
/// Create a new MediaBuilder from a reader `data`
|
||||
pub fn from_reader<R: Read + Send + 'static>(data: R) -> MediaBuilder {
|
||||
pub fn from_reader<R: AsyncRead + Send + Sync + 'static>(data: R) -> MediaBuilder {
|
||||
MediaBuilder {
|
||||
data: MediaBuilderData::Reader(Box::from(data)),
|
||||
data: MediaBuilderData::Reader(Box::pin(data)),
|
||||
filename: None,
|
||||
mimetype: None,
|
||||
description: None,
|
||||
|
||||
+8
-8
@@ -1,7 +1,7 @@
|
||||
use super::{deserialise, Mastodon, Result};
|
||||
use super::{deserialise_response, Mastodon, Result};
|
||||
use crate::entities::itemsiter::ItemsIter;
|
||||
use hyper_old_types::header::{parsing, Link, RelationType};
|
||||
use reqwest::{blocking::Response, header::LINK};
|
||||
use reqwest::{Response, header::LINK};
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
@@ -11,7 +11,7 @@ macro_rules! pages {
|
||||
$(
|
||||
doc_comment::doc_comment!(concat!(
|
||||
"Method to retrieve the ", stringify!($direction), " page of results"),
|
||||
pub fn $fun(&mut self) -> Result<Option<Vec<T>>> {
|
||||
pub async fn $fun(&mut self) -> Result<Option<Vec<T>>> {
|
||||
let url = match self.$direction.take() {
|
||||
Some(s) => s,
|
||||
None => return Ok(None),
|
||||
@@ -19,13 +19,13 @@ macro_rules! pages {
|
||||
|
||||
let response = self.mastodon.send(
|
||||
self.mastodon.client.get(url)
|
||||
)?;
|
||||
).await?;
|
||||
|
||||
let (prev, next) = get_links(&response)?;
|
||||
self.next = next;
|
||||
self.prev = prev;
|
||||
|
||||
deserialise(response)
|
||||
deserialise_response(response).await
|
||||
});
|
||||
)*
|
||||
}
|
||||
@@ -107,10 +107,10 @@ impl<'a, T: for<'de> Deserialize<'de>> Page<'a, T> {
|
||||
prev: prev_page
|
||||
}
|
||||
|
||||
pub(crate) fn new(mastodon: &'a Mastodon, response: Response) -> Result<Self> {
|
||||
pub(crate) async fn new<'m>(mastodon: &'m Mastodon, response: Response) -> Result<Page<'m, T>> {
|
||||
let (prev, next) = get_links(&response)?;
|
||||
Ok(Page {
|
||||
initial_items: deserialise(response)?,
|
||||
initial_items: deserialise_response(response).await?,
|
||||
next,
|
||||
prev,
|
||||
mastodon,
|
||||
@@ -189,7 +189,7 @@ impl<'a, T: Clone + for<'de> Deserialize<'de>> Page<'a, T> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn items_iter(self) -> impl Iterator<Item = T> + 'a
|
||||
pub fn items_iter(self) -> ItemsIter<'a, T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
|
||||
+14
-13
@@ -1,6 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use reqwest::blocking::{Client, RequestBuilder, Response};
|
||||
use reqwest::{Client, RequestBuilder, Response};
|
||||
use serde::Deserialize;
|
||||
use std::convert::TryInto;
|
||||
|
||||
@@ -93,9 +93,9 @@ impl<'a> Registration<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
async fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
let req = req.build()?;
|
||||
Ok(self.client.execute(req)?)
|
||||
Ok(self.client.execute(req).await?)
|
||||
}
|
||||
|
||||
/// Register the given application
|
||||
@@ -119,12 +119,12 @@ impl<'a> Registration<'a> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn register<I: TryInto<App>>(&mut self, app: I) -> Result<Registered>
|
||||
pub async fn register<I: TryInto<App>>(&mut self, app: I) -> Result<Registered>
|
||||
where
|
||||
Error: From<<I as TryInto<App>>::Error>,
|
||||
{
|
||||
let app = app.try_into()?;
|
||||
let oauth = self.send_app(&app)?;
|
||||
let oauth = self.send_app(&app).await?;
|
||||
|
||||
Ok(Registered {
|
||||
base: self.base.clone(),
|
||||
@@ -157,9 +157,9 @@ impl<'a> Registration<'a> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn build(&mut self) -> Result<Registered> {
|
||||
pub async fn build(&mut self) -> Result<Registered> {
|
||||
let app: App = self.app_builder.clone().build()?;
|
||||
let oauth = self.send_app(&app)?;
|
||||
let oauth = self.send_app(&app).await?;
|
||||
|
||||
Ok(Registered {
|
||||
base: self.base.clone(),
|
||||
@@ -172,9 +172,10 @@ impl<'a> Registration<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn send_app(&self, app: &App) -> Result<OAuth> {
|
||||
async fn send_app(&self, app: &App) -> Result<OAuth> {
|
||||
let url = format!("{}/api/v1/apps", self.base);
|
||||
Ok(self.send(self.client.post(&url).json(&app))?.json()?)
|
||||
Ok(self.send(self.client.post(&url).json(&app)).await?
|
||||
.json().await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +229,9 @@ impl Registered {
|
||||
}
|
||||
|
||||
impl Registered {
|
||||
fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
async fn send(&self, req: RequestBuilder) -> Result<Response> {
|
||||
let req = req.build()?;
|
||||
Ok(self.client.execute(req)?)
|
||||
Ok(self.client.execute(req).await?)
|
||||
}
|
||||
|
||||
/// Returns the parts of the `Registered` struct that can be used to
|
||||
@@ -298,14 +299,14 @@ impl Registered {
|
||||
|
||||
/// Create an access token from the client id, client secret, and code
|
||||
/// provided by the authorisation url.
|
||||
pub fn complete(&self, code: &str) -> Result<Mastodon> {
|
||||
pub async fn complete(&self, code: &str) -> Result<Mastodon> {
|
||||
let url = format!(
|
||||
"{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&\
|
||||
redirect_uri={}",
|
||||
self.base, self.client_id, self.client_secret, code, self.redirect
|
||||
);
|
||||
|
||||
let token: AccessToken = self.send(self.client.post(&url))?.json()?;
|
||||
let token: AccessToken = self.send(self.client.post(&url)).await?.json().await?;
|
||||
|
||||
let data = Data {
|
||||
base: self.base.clone().into(),
|
||||
|
||||
Reference in New Issue
Block a user