Introduce HttpSend trait for converting Request -> Response
Parameterize everything that involves sending HTTP requests with the `H: HttpSend` bound. This will allow us to swap out `HttpSend` implementations when necessary, in order to better test our code
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
use page::Page;
|
use page::Page;
|
||||||
|
use http_send::HttpSend;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// Abstracts away the `next_page` logic into a single stream of items
|
/// Abstracts away the `next_page` logic into a single stream of items
|
||||||
@@ -23,15 +24,15 @@ use serde::Deserialize;
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) struct ItemsIter<'a, T: Clone + for<'de> Deserialize<'de>> {
|
pub(crate) struct ItemsIter<'a, T: Clone + for<'de> Deserialize<'de>, H: 'a + HttpSend> {
|
||||||
page: Page<'a, T>,
|
page: Page<'a, T, H>,
|
||||||
buffer: Vec<T>,
|
buffer: Vec<T>,
|
||||||
cur_idx: usize,
|
cur_idx: usize,
|
||||||
use_initial: bool,
|
use_initial: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Clone + for<'de> Deserialize<'de>> ItemsIter<'a, T> {
|
impl<'a, T: Clone + for<'de> Deserialize<'de>, H: HttpSend> ItemsIter<'a, T, H> {
|
||||||
pub(crate) fn new(page: Page<'a, T>) -> ItemsIter<'a, T> {
|
pub(crate) fn new(page: Page<'a, T, H>) -> ItemsIter<'a, T, H> {
|
||||||
ItemsIter {
|
ItemsIter {
|
||||||
page,
|
page,
|
||||||
buffer: vec![],
|
buffer: vec![],
|
||||||
@@ -60,7 +61,7 @@ impl<'a, T: Clone + for<'de> Deserialize<'de>> ItemsIter<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Clone + for<'de> Deserialize<'de>> Iterator for ItemsIter<'a, T> {
|
impl<'a, T: Clone + for<'de> Deserialize<'de>, H: HttpSend> Iterator for ItemsIter<'a, T, H> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use Result;
|
||||||
|
use reqwest::{Client, Request, RequestBuilder, Response};
|
||||||
|
|
||||||
|
pub trait HttpSend {
|
||||||
|
fn execute(&self, client: &Client, request: Request) -> Result<Response>;
|
||||||
|
fn send(&self, client: &Client, builder: &mut RequestBuilder) -> Result<Response> {
|
||||||
|
let request = builder.build()?;
|
||||||
|
self.execute(client, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HttpSender;
|
||||||
|
|
||||||
|
impl HttpSend for HttpSender {
|
||||||
|
fn execute(&self, client: &Client, request: Request) -> Result<Response> {
|
||||||
|
Ok(client.execute(request)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
-46
@@ -50,10 +50,12 @@ use reqwest::{
|
|||||||
header::{Authorization, Bearer, Headers},
|
header::{Authorization, Bearer, Headers},
|
||||||
Client,
|
Client,
|
||||||
Response,
|
Response,
|
||||||
|
RequestBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use entities::prelude::*;
|
use entities::prelude::*;
|
||||||
use page::Page;
|
use page::Page;
|
||||||
|
use http_send::{HttpSend, HttpSender};
|
||||||
|
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
pub use errors::{ApiError, Error, Result};
|
pub use errors::{ApiError, Error, Result};
|
||||||
@@ -69,6 +71,8 @@ pub mod data;
|
|||||||
pub mod entities;
|
pub mod entities;
|
||||||
/// Errors
|
/// Errors
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
/// Contains trait for converting `reqwest::Request`s to `reqwest::Response`s
|
||||||
|
pub mod http_send;
|
||||||
/// Handling multiple pages of entities.
|
/// Handling multiple pages of entities.
|
||||||
pub mod page;
|
pub mod page;
|
||||||
/// Registering your app.
|
/// Registering your app.
|
||||||
@@ -90,8 +94,9 @@ pub mod prelude {
|
|||||||
|
|
||||||
/// Your mastodon application client, handles all requests to and from Mastodon.
|
/// Your mastodon application client, handles all requests to and from Mastodon.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Mastodon {
|
pub struct Mastodon<H: HttpSend = HttpSender> {
|
||||||
client: Client,
|
client: Client,
|
||||||
|
http_sender: H,
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
/// Raw data about your mastodon instance.
|
/// Raw data about your mastodon instance.
|
||||||
pub data: Data,
|
pub data: Data,
|
||||||
@@ -100,35 +105,35 @@ pub struct Mastodon {
|
|||||||
/// Represents the set of methods that a Mastodon Client can do, so that
|
/// Represents the set of methods that a Mastodon Client can do, so that
|
||||||
/// implementations might be swapped out for testing
|
/// implementations might be swapped out for testing
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub trait MastodonClient {
|
pub trait MastodonClient<H: HttpSend = HttpSender> {
|
||||||
fn favourites(&self) -> Result<Page<Status>> {
|
fn favourites(&self) -> Result<Page<Status, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn blocks(&self) -> Result<Page<Account>> {
|
fn blocks(&self) -> Result<Page<Account, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn domain_blocks(&self) -> Result<Page<String>> {
|
fn domain_blocks(&self) -> Result<Page<String, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn follow_requests(&self) -> Result<Page<Account>> {
|
fn follow_requests(&self) -> Result<Page<Account, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn get_home_timeline(&self) -> Result<Page<Status>> {
|
fn get_home_timeline(&self) -> Result<Page<Status, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn get_emojis(&self) -> Result<Page<Emoji>> {
|
fn get_emojis(&self) -> Result<Page<Emoji, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn mutes(&self) -> Result<Page<Account>> {
|
fn mutes(&self) -> Result<Page<Account, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn notifications(&self) -> Result<Page<Notification>> {
|
fn notifications(&self) -> Result<Page<Notification, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn reports(&self) -> Result<Page<Report>> {
|
fn reports(&self) -> Result<Page<Report, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn followers(&self, id: &str) -> Result<Page<Account>> {
|
fn followers(&self, id: &str) -> Result<Page<Account, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn following(&self) -> Result<Account> {
|
fn following(&self) -> Result<Account> {
|
||||||
@@ -233,13 +238,13 @@ pub trait MastodonClient {
|
|||||||
fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> {
|
fn get_tagged_timeline(&self, hashtag: String, local: bool) -> Result<Vec<Status>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status>>
|
fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status, H>>
|
||||||
where
|
where
|
||||||
S: Into<Option<StatusesRequest<'a>>>,
|
S: Into<Option<StatusesRequest<'a>>>,
|
||||||
{
|
{
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship>> {
|
fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
fn search_accounts(
|
fn search_accounts(
|
||||||
@@ -247,25 +252,30 @@ pub trait MastodonClient {
|
|||||||
query: &str,
|
query: &str,
|
||||||
limit: Option<u64>,
|
limit: Option<u64>,
|
||||||
following: bool,
|
following: bool,
|
||||||
) -> Result<Page<Account>> {
|
) -> Result<Page<Account, H>> {
|
||||||
unimplemented!("This method was not implemented");
|
unimplemented!("This method was not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mastodon {
|
impl<H: HttpSend> Mastodon<H> {
|
||||||
methods![get, post, delete,];
|
methods![get, post, delete,];
|
||||||
|
|
||||||
fn route(&self, url: &str) -> String {
|
fn route(&self, url: &str) -> String {
|
||||||
let mut s = (*self.base).to_owned();
|
format!("{}{}", self.base, url)
|
||||||
s += url;
|
}
|
||||||
s
|
|
||||||
|
pub(crate) fn send(&self, req: &mut RequestBuilder) -> Result<Response> {
|
||||||
|
Ok(self.http_sender.send(
|
||||||
|
&self.client,
|
||||||
|
req.headers(self.headers.clone())
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Data> for Mastodon {
|
impl From<Data> for Mastodon<HttpSender> {
|
||||||
/// Creates a mastodon instance from the data struct.
|
/// Creates a mastodon instance from the data struct.
|
||||||
fn from(data: Data) -> Mastodon {
|
fn from(data: Data) -> Mastodon<HttpSender> {
|
||||||
let mut builder = MastodonBuilder::new();
|
let mut builder = MastodonBuilder::new(HttpSender);
|
||||||
builder.data(data);
|
builder.data(data);
|
||||||
builder
|
builder
|
||||||
.build()
|
.build()
|
||||||
@@ -273,7 +283,7 @@ impl From<Data> for Mastodon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MastodonClient for Mastodon {
|
impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
|
||||||
paged_routes! {
|
paged_routes! {
|
||||||
(get) favourites: "favourites" => Status,
|
(get) favourites: "favourites" => Status,
|
||||||
(get) blocks: "blocks" => Account,
|
(get) blocks: "blocks" => Account,
|
||||||
@@ -328,12 +338,11 @@ impl MastodonClient for Mastodon {
|
|||||||
|
|
||||||
fn update_credentials(&self, changes: CredientialsBuilder) -> Result<Account> {
|
fn update_credentials(&self, changes: CredientialsBuilder) -> Result<Account> {
|
||||||
let url = self.route("/api/v1/accounts/update_credentials");
|
let url = self.route("/api/v1/accounts/update_credentials");
|
||||||
let response = self
|
let response = self.send(
|
||||||
.client
|
self.client
|
||||||
.patch(&url)
|
.patch(&url)
|
||||||
.headers(self.headers.clone())
|
.multipart(changes.into_form()?)
|
||||||
.multipart(changes.into_form()?)
|
)?;
|
||||||
.send()?;
|
|
||||||
|
|
||||||
let status = response.status().clone();
|
let status = response.status().clone();
|
||||||
|
|
||||||
@@ -348,12 +357,11 @@ impl MastodonClient for Mastodon {
|
|||||||
|
|
||||||
/// Post a new status to the account.
|
/// Post a new status to the account.
|
||||||
fn new_status(&self, status: StatusBuilder) -> Result<Status> {
|
fn new_status(&self, status: StatusBuilder) -> Result<Status> {
|
||||||
let response = self
|
let response = self.send(
|
||||||
.client
|
self.client
|
||||||
.post(&self.route("/api/v1/statuses"))
|
.post(&self.route("/api/v1/statuses"))
|
||||||
.headers(self.headers.clone())
|
.json(&status)
|
||||||
.json(&status)
|
)?;
|
||||||
.send()?;
|
|
||||||
|
|
||||||
deserialise(response)
|
deserialise(response)
|
||||||
}
|
}
|
||||||
@@ -423,7 +431,7 @@ impl MastodonClient for Mastodon {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status>>
|
fn statuses<'a, 'b: 'a, S>(&'b self, id: &'b str, request: S) -> Result<Page<Status, H>>
|
||||||
where
|
where
|
||||||
S: Into<Option<StatusesRequest<'a>>>,
|
S: Into<Option<StatusesRequest<'a>>>,
|
||||||
{
|
{
|
||||||
@@ -433,14 +441,16 @@ impl MastodonClient for Mastodon {
|
|||||||
url = format!("{}{}", url, request.to_querystring());
|
url = format!("{}{}", url, request.to_querystring());
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = self.client.get(&url).headers(self.headers.clone()).send()?;
|
let response = self.send(
|
||||||
|
&mut self.client.get(&url)
|
||||||
|
)?;
|
||||||
|
|
||||||
Page::new(self, response)
|
Page::new(self, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the client account's relationship to a list of other accounts.
|
/// Returns the client account's relationship to a list of other accounts.
|
||||||
/// Such as whether they follow them or vice versa.
|
/// Such as whether they follow them or vice versa.
|
||||||
fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship>> {
|
fn relationships(&self, ids: &[&str]) -> Result<Page<Relationship, H>> {
|
||||||
let mut url = self.route("/api/v1/accounts/relationships?");
|
let mut url = self.route("/api/v1/accounts/relationships?");
|
||||||
|
|
||||||
if ids.len() == 1 {
|
if ids.len() == 1 {
|
||||||
@@ -455,7 +465,9 @@ impl MastodonClient for Mastodon {
|
|||||||
url.pop();
|
url.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = self.client.get(&url).headers(self.headers.clone()).send()?;
|
let response = self.send(
|
||||||
|
&mut self.client.get(&url)
|
||||||
|
)?;
|
||||||
|
|
||||||
Page::new(self, response)
|
Page::new(self, response)
|
||||||
}
|
}
|
||||||
@@ -468,7 +480,7 @@ impl MastodonClient for Mastodon {
|
|||||||
query: &str,
|
query: &str,
|
||||||
limit: Option<u64>,
|
limit: Option<u64>,
|
||||||
following: bool,
|
following: bool,
|
||||||
) -> Result<Page<Account>> {
|
) -> Result<Page<Account, H>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/api/v1/accounts/search?q={}&limit={}&following={}",
|
"{}/api/v1/accounts/search?q={}&limit={}&following={}",
|
||||||
self.base,
|
self.base,
|
||||||
@@ -477,13 +489,15 @@ impl MastodonClient for Mastodon {
|
|||||||
following
|
following
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = self.client.get(&url).headers(self.headers.clone()).send()?;
|
let response = self.send(
|
||||||
|
&mut self.client.get(&url)
|
||||||
|
)?;
|
||||||
|
|
||||||
Page::new(self, response)
|
Page::new(self, response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Deref for Mastodon {
|
impl<H: HttpSend> ops::Deref for Mastodon<H> {
|
||||||
type Target = Data;
|
type Target = Data;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -491,14 +505,16 @@ impl ops::Deref for Mastodon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MastodonBuilder {
|
struct MastodonBuilder<H: HttpSend> {
|
||||||
client: Option<Client>,
|
client: Option<Client>,
|
||||||
|
http_sender: H,
|
||||||
data: Option<Data>,
|
data: Option<Data>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MastodonBuilder {
|
impl<H: HttpSend> MastodonBuilder<H> {
|
||||||
pub fn new() -> Self {
|
pub fn new(sender: H) -> Self {
|
||||||
MastodonBuilder {
|
MastodonBuilder {
|
||||||
|
http_sender: sender,
|
||||||
client: None,
|
client: None,
|
||||||
data: None,
|
data: None,
|
||||||
}
|
}
|
||||||
@@ -515,7 +531,7 @@ impl MastodonBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<Mastodon> {
|
pub fn build(self) -> Result<Mastodon<H>> {
|
||||||
Ok(if let Some(data) = self.data {
|
Ok(if let Some(data) = self.data {
|
||||||
let mut headers = Headers::new();
|
let mut headers = Headers::new();
|
||||||
headers.set(Authorization(Bearer {
|
headers.set(Authorization(Bearer {
|
||||||
@@ -524,6 +540,7 @@ impl MastodonBuilder {
|
|||||||
|
|
||||||
Mastodon {
|
Mastodon {
|
||||||
client: self.client.unwrap_or_else(|| Client::new()),
|
client: self.client.unwrap_or_else(|| Client::new()),
|
||||||
|
http_sender: self.http_sender,
|
||||||
headers,
|
headers,
|
||||||
data,
|
data,
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-19
@@ -4,9 +4,9 @@ macro_rules! methods {
|
|||||||
fn $method<T: for<'de> serde::Deserialize<'de>>(&self, url: String)
|
fn $method<T: for<'de> serde::Deserialize<'de>>(&self, url: String)
|
||||||
-> Result<T>
|
-> Result<T>
|
||||||
{
|
{
|
||||||
let response = self.client.$method(&url)
|
let response = self.send(
|
||||||
.headers(self.headers.clone())
|
&mut self.client.$method(&url)
|
||||||
.send()?;
|
)?;
|
||||||
|
|
||||||
deserialise(response)
|
deserialise(response)
|
||||||
}
|
}
|
||||||
@@ -22,11 +22,11 @@ macro_rules! paged_routes {
|
|||||||
"Equivalent to `/api/v1/",
|
"Equivalent to `/api/v1/",
|
||||||
$url,
|
$url,
|
||||||
"`\n# Errors\nIf `access_token` is not set."),
|
"`\n# Errors\nIf `access_token` is not set."),
|
||||||
fn $name(&self) -> Result<Page<$ret>> {
|
fn $name(&self) -> Result<Page<$ret, H>> {
|
||||||
let url = self.route(concat!("/api/v1/", $url));
|
let url = self.route(concat!("/api/v1/", $url));
|
||||||
let response = self.client.$method(&url)
|
let response = self.send(
|
||||||
.headers(self.headers.clone())
|
&mut self.client.$method(&url)
|
||||||
.send()?;
|
)?;
|
||||||
|
|
||||||
Page::new(self, response)
|
Page::new(self, response)
|
||||||
}
|
}
|
||||||
@@ -55,10 +55,11 @@ macro_rules! route {
|
|||||||
.file(stringify!($param), $param.as_ref())?
|
.file(stringify!($param), $param.as_ref())?
|
||||||
)*;
|
)*;
|
||||||
|
|
||||||
let response = self.client.post(&self.route(concat!("/api/v1/", $url)))
|
let response = self.send(
|
||||||
.headers(self.headers.clone())
|
self.client
|
||||||
.multipart(form_data)
|
.post(&self.route(concat!("/api/v1/", $url)))
|
||||||
.send()?;
|
.multipart(form_data)
|
||||||
|
)?;
|
||||||
|
|
||||||
let status = response.status().clone();
|
let status = response.status().clone();
|
||||||
|
|
||||||
@@ -90,10 +91,10 @@ macro_rules! route {
|
|||||||
)*
|
)*
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = self.client.$method(&self.route(concat!("/api/v1/", $url)))
|
let response = self.send(
|
||||||
.headers(self.headers.clone())
|
self.client.$method(&self.route(concat!("/api/v1/", $url)))
|
||||||
.json(&form_data)
|
.json(&form_data)
|
||||||
.send()?;
|
)?;
|
||||||
|
|
||||||
let status = response.status().clone();
|
let status = response.status().clone();
|
||||||
|
|
||||||
@@ -152,11 +153,11 @@ macro_rules! paged_routes_with_id {
|
|||||||
"Equivalent to `/api/v1/",
|
"Equivalent to `/api/v1/",
|
||||||
$url,
|
$url,
|
||||||
"`\n# Errors\nIf `access_token` is not set."),
|
"`\n# Errors\nIf `access_token` is not set."),
|
||||||
fn $name(&self, id: &str) -> Result<Page<$ret>> {
|
fn $name(&self, id: &str) -> Result<Page<$ret, H>> {
|
||||||
let url = self.route(&format!(concat!("/api/v1/", $url), id));
|
let url = self.route(&format!(concat!("/api/v1/", $url), id));
|
||||||
let response = self.client.$method(&url)
|
let response = self.send(
|
||||||
.headers(self.headers.clone())
|
&mut self.client.$method(&url)
|
||||||
.send()?;
|
)?;
|
||||||
|
|
||||||
Page::new(self, response)
|
Page::new(self, response)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -7,8 +7,10 @@ use reqwest::{
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub struct Page<'a, T: for<'de> Deserialize<'de>> {
|
use http_send::HttpSend;
|
||||||
mastodon: &'a Mastodon,
|
|
||||||
|
pub struct Page<'a, T: for<'de> Deserialize<'de>, H: 'a + HttpSend> {
|
||||||
|
mastodon: &'a Mastodon<H>,
|
||||||
next: Option<Url>,
|
next: Option<Url>,
|
||||||
prev: Option<Url>,
|
prev: Option<Url>,
|
||||||
/// Initial set of items
|
/// Initial set of items
|
||||||
@@ -25,9 +27,9 @@ macro_rules! pages {
|
|||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.mastodon.client.get(url)
|
let response = self.mastodon.send(
|
||||||
.headers(self.mastodon.headers.clone())
|
&mut self.mastodon.client.get(url)
|
||||||
.send()?;
|
)?;
|
||||||
|
|
||||||
let (prev, next) = get_links(&response)?;
|
let (prev, next) = get_links(&response)?;
|
||||||
self.next = next;
|
self.next = next;
|
||||||
@@ -39,13 +41,13 @@ macro_rules! pages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: for<'de> Deserialize<'de>> Page<'a, T> {
|
impl<'a, T: for<'de> Deserialize<'de>, H: HttpSend> Page<'a, T, H> {
|
||||||
pages! {
|
pages! {
|
||||||
next: next_page,
|
next: next_page,
|
||||||
prev: prev_page
|
prev: prev_page
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(mastodon: &'a Mastodon, response: Response) -> Result<Self> {
|
pub fn new(mastodon: &'a Mastodon<H>, response: Response) -> Result<Self> {
|
||||||
let (prev, next) = get_links(&response)?;
|
let (prev, next) = get_links(&response)?;
|
||||||
Ok(Page {
|
Ok(Page {
|
||||||
initial_items: deserialise(response)?,
|
initial_items: deserialise(response)?,
|
||||||
@@ -56,7 +58,7 @@ impl<'a, T: for<'de> Deserialize<'de>> Page<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Clone + for<'de> Deserialize<'de>> Page<'a, T> {
|
impl<'a, T: Clone + for<'de> Deserialize<'de>, H: HttpSend> Page<'a, T, H> {
|
||||||
/// Returns an iterator that provides a stream of `T`s
|
/// Returns an iterator that provides a stream of `T`s
|
||||||
///
|
///
|
||||||
/// This abstracts away the process of iterating over each item in a page,
|
/// This abstracts away the process of iterating over each item in a page,
|
||||||
|
|||||||
+44
-10
@@ -1,4 +1,4 @@
|
|||||||
use reqwest::Client;
|
use reqwest::{Client, RequestBuilder, Response};
|
||||||
use try_from::TryInto;
|
use try_from::TryInto;
|
||||||
|
|
||||||
use apps::{App, Scopes};
|
use apps::{App, Scopes};
|
||||||
@@ -7,12 +7,14 @@ use Error;
|
|||||||
use Mastodon;
|
use Mastodon;
|
||||||
use MastodonBuilder;
|
use MastodonBuilder;
|
||||||
use Result;
|
use Result;
|
||||||
|
use http_send::{HttpSend, HttpSender};
|
||||||
|
|
||||||
/// Handles registering your mastodon app to your instance. It is recommended
|
/// Handles registering your mastodon app to your instance. It is recommended
|
||||||
/// you cache your data struct to avoid registering on every run.
|
/// you cache your data struct to avoid registering on every run.
|
||||||
pub struct Registration {
|
pub struct Registration<H: HttpSend> {
|
||||||
base: String,
|
base: String,
|
||||||
client: Client,
|
client: Client,
|
||||||
|
http_sender: H,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -32,7 +34,7 @@ struct AccessToken {
|
|||||||
access_token: String,
|
access_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Registration {
|
impl Registration<HttpSender> {
|
||||||
/// Construct a new registration process to the instance of the `base` url.
|
/// Construct a new registration process to the instance of the `base` url.
|
||||||
/// ```
|
/// ```
|
||||||
/// use elefren::apps::prelude::*;
|
/// use elefren::apps::prelude::*;
|
||||||
@@ -43,8 +45,27 @@ impl Registration {
|
|||||||
Registration {
|
Registration {
|
||||||
base: base.into(),
|
base: base.into(),
|
||||||
client: Client::new(),
|
client: Client::new(),
|
||||||
|
http_sender: HttpSender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: HttpSend> Registration<H> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn with_sender<I: Into<String>>(base: I, http_sender: H) -> Self {
|
||||||
|
Registration {
|
||||||
|
base: base.into(),
|
||||||
|
client: Client::new(),
|
||||||
|
http_sender: http_sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(&self, req: &mut RequestBuilder) -> Result<Response> {
|
||||||
|
Ok(self.http_sender.send(
|
||||||
|
&self.client,
|
||||||
|
req
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Register the application with the server from the `base` url.
|
/// Register the application with the server from the `base` url.
|
||||||
///
|
///
|
||||||
@@ -69,13 +90,15 @@ impl Registration {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn register<I: TryInto<App>>(self, app: I) -> Result<Registered>
|
pub fn register<I: TryInto<App>>(self, app: I) -> Result<Registered<H>>
|
||||||
where
|
where
|
||||||
Error: From<<I as TryInto<App>>::Err>,
|
Error: From<<I as TryInto<App>>::Err>,
|
||||||
{
|
{
|
||||||
let app = app.try_into()?;
|
let app = app.try_into()?;
|
||||||
let url = format!("{}/api/v1/apps", self.base);
|
let url = format!("{}/api/v1/apps", self.base);
|
||||||
let oauth: OAuth = self.client.post(&url).form(&app).send()?.json()?;
|
let oauth: OAuth = self.send(
|
||||||
|
self.client.post(&url).form(&app)
|
||||||
|
)?.json()?;
|
||||||
|
|
||||||
Ok(Registered {
|
Ok(Registered {
|
||||||
base: self.base,
|
base: self.base,
|
||||||
@@ -84,11 +107,19 @@ impl Registration {
|
|||||||
client_secret: oauth.client_secret,
|
client_secret: oauth.client_secret,
|
||||||
redirect: oauth.redirect_uri,
|
redirect: oauth.redirect_uri,
|
||||||
scopes: app.scopes(),
|
scopes: app.scopes(),
|
||||||
|
http_sender: self.http_sender,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Registered {
|
impl<H: HttpSend> Registered<H> {
|
||||||
|
fn send(&self, req: &mut RequestBuilder) -> Result<Response> {
|
||||||
|
Ok(self.http_sender.send(
|
||||||
|
&self.client,
|
||||||
|
req
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the full url needed for authorisation. This needs to be opened
|
/// Returns the full url needed for authorisation. This needs to be opened
|
||||||
/// in a browser.
|
/// in a browser.
|
||||||
pub fn authorize_url(&self) -> Result<String> {
|
pub fn authorize_url(&self) -> Result<String> {
|
||||||
@@ -102,7 +133,7 @@ impl Registered {
|
|||||||
|
|
||||||
/// Create an access token from the client id, client secret, and code
|
/// Create an access token from the client id, client secret, and code
|
||||||
/// provided by the authorisation url.
|
/// provided by the authorisation url.
|
||||||
pub fn complete(self, code: String) -> Result<Mastodon> {
|
pub fn complete(self, code: String) -> Result<Mastodon<H>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&redirect_uri={}",
|
"{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&redirect_uri={}",
|
||||||
self.base,
|
self.base,
|
||||||
@@ -112,7 +143,9 @@ impl Registered {
|
|||||||
self.redirect
|
self.redirect
|
||||||
);
|
);
|
||||||
|
|
||||||
let token: AccessToken = self.client.post(&url).send()?.json()?;
|
let token: AccessToken = self.send(
|
||||||
|
&mut self.client.post(&url)
|
||||||
|
)?.json()?;
|
||||||
|
|
||||||
let data = Data {
|
let data = Data {
|
||||||
base: self.base.into(),
|
base: self.base.into(),
|
||||||
@@ -122,17 +155,18 @@ impl Registered {
|
|||||||
token: token.access_token.into(),
|
token: token.access_token.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut builder = MastodonBuilder::new();
|
let mut builder = MastodonBuilder::new(self.http_sender);
|
||||||
builder.client(self.client).data(data);
|
builder.client(self.client).data(data);
|
||||||
Ok(builder.build()?)
|
Ok(builder.build()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Registered {
|
pub struct Registered<H: HttpSend> {
|
||||||
base: String,
|
base: String,
|
||||||
client: Client,
|
client: Client,
|
||||||
client_id: String,
|
client_id: String,
|
||||||
client_secret: String,
|
client_secret: String,
|
||||||
redirect: String,
|
redirect: String,
|
||||||
scopes: Scopes,
|
scopes: Scopes,
|
||||||
|
http_sender: H,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user