Improve remote error handling
This commit is contained in:
parent
58ffee1970
commit
ed497d96d4
|
@ -70,10 +70,11 @@ features = ["io"]
|
||||||
tokio-test = "0.4.2"
|
tokio-test = "0.4.2"
|
||||||
futures-util = "0.3.25"
|
futures-util = "0.3.25"
|
||||||
indoc = "1.0"
|
indoc = "1.0"
|
||||||
pretty_env_logger = "0.3.0"
|
|
||||||
skeptic = "0.13"
|
skeptic = "0.13"
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
# for examples:
|
||||||
femme = "2.2.1"
|
femme = "2.2.1"
|
||||||
|
html2text = "0.4.4"
|
||||||
|
|
||||||
[build-dependencies.skeptic]
|
[build-dependencies.skeptic]
|
||||||
version = "0.13"
|
version = "0.13"
|
||||||
|
|
28
README.md
28
README.md
|
@ -38,18 +38,16 @@ features = ["toml"]
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
// src/main.rs
|
// src/main.rs
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use mastodon_async::prelude::*;
|
use mastodon_async::prelude::*;
|
||||||
use mastodon_async::helpers::toml; // requires `features = ["toml"]`
|
use mastodon_async::helpers::toml; // requires `features = ["toml"]`
|
||||||
use mastodon_async::helpers::cli;
|
use mastodon_async::{helpers::cli, Result};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<()> {
|
||||||
let mastodon = if let Ok(data) = toml::from_file("mastodon-data.toml") {
|
let mastodon = if let Ok(data) = toml::from_file("mastodon-data.toml") {
|
||||||
Mastodon::from(data)
|
Mastodon::from(data)
|
||||||
} else {
|
} else {
|
||||||
register()?
|
register().await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let you = mastodon.verify_credentials().await?;
|
let you = mastodon.verify_credentials().await?;
|
||||||
|
@ -59,14 +57,15 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register() -> Result<Mastodon, Box<dyn Error>> {
|
async fn register() -> Result<Mastodon> {
|
||||||
let registration = Registration::new("https://botsin.space")
|
let registration = Registration::new("https://botsin.space")
|
||||||
.client_name("mastodon-async-examples")
|
.client_name("mastodon-async-examples")
|
||||||
.build()?;
|
.build()
|
||||||
let mastodon = cli::authenticate(registration)?;
|
.await?;
|
||||||
|
let mastodon = cli::authenticate(registration).await?;
|
||||||
|
|
||||||
// Save app data for using on the next run.
|
// Save app data for using on the next run.
|
||||||
toml::to_file(&*mastodon, "mastodon-data.toml")?;
|
toml::to_file(&mastodon.data, "mastodon-data.toml")?;
|
||||||
|
|
||||||
Ok(mastodon)
|
Ok(mastodon)
|
||||||
}
|
}
|
||||||
|
@ -75,24 +74,23 @@ fn register() -> Result<Mastodon, Box<dyn Error>> {
|
||||||
It also supports the [Streaming API](https://docs.joinmastodon.org/api/streaming):
|
It also supports the [Streaming API](https://docs.joinmastodon.org/api/streaming):
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use mastodon_async::prelude::*;
|
use mastodon_async::{prelude::*, Result, entities::event::Event};
|
||||||
use mastodon_async::entities::event::Event;
|
use futures_util::TryStreamExt;
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<Error>> {
|
async fn main() -> Result<()> {
|
||||||
let client = Mastodon::from(Data::default());
|
let client = Mastodon::from(Data::default());
|
||||||
|
|
||||||
client.stream_user()
|
client.stream_user()
|
||||||
.await?
|
.await?
|
||||||
.try_for_each(|event| {
|
.try_for_each(|event| async move {
|
||||||
match event {
|
match event {
|
||||||
Event::Update(ref status) => { /* .. */ },
|
Event::Update(ref status) => { /* .. */ },
|
||||||
Event::Notification(ref notification) => { /* .. */ },
|
Event::Notification(ref notification) => { /* .. */ },
|
||||||
Event::Delete(ref id) => { /* .. */ },
|
Event::Delete(ref id) => { /* .. */ },
|
||||||
Event::FiltersChanged => { /* .. */ },
|
Event::FiltersChanged => { /* .. */ },
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
||||||
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
||||||
#[macro_use]
|
|
||||||
extern crate pretty_env_logger;
|
|
||||||
mod register;
|
mod register;
|
||||||
|
use mastodon_async::Result;
|
||||||
use register::Mastodon;
|
|
||||||
use std::error;
|
|
||||||
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
fn main() -> Result<(), Box<error::Error>> {
|
#[tokio::main]
|
||||||
let mastodon = register::get_mastodon_data()?;
|
async fn main() -> Result<()> {
|
||||||
|
let mastodon = register::get_mastodon_data().await?;
|
||||||
let input = register::read_line("Enter the account id you'd like to follow: ")?;
|
let input = register::read_line("Enter the account id you'd like to follow: ")?;
|
||||||
let new_follow = mastodon.follow(input.trim())?;
|
let new_follow = mastodon.follow(input.trim()).await?;
|
||||||
|
|
||||||
println!("{:#?}", new_follow);
|
println!("{:#?}", new_follow);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
||||||
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
||||||
#[macro_use]
|
|
||||||
extern crate pretty_env_logger;
|
|
||||||
mod register;
|
mod register;
|
||||||
|
use mastodon_async::Result;
|
||||||
use register::Mastodon;
|
|
||||||
use std::error;
|
|
||||||
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
fn main() -> Result<(), Box<error::Error>> {
|
#[tokio::main]
|
||||||
let mastodon = register::get_mastodon_data()?;
|
async fn main() -> Result<()> {
|
||||||
for account in mastodon.follows_me()?.items_iter() {
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
let mastodon = register::get_mastodon_data().await?;
|
||||||
|
mastodon
|
||||||
|
.follows_me()
|
||||||
|
.await?
|
||||||
|
.items_iter()
|
||||||
|
.for_each(|account| async move {
|
||||||
println!("{}", account.acct);
|
println!("{}", account.acct);
|
||||||
}
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
||||||
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
||||||
#[macro_use]
|
|
||||||
extern crate pretty_env_logger;
|
|
||||||
mod register;
|
mod register;
|
||||||
|
use futures_util::StreamExt;
|
||||||
use register::Mastodon;
|
use mastodon_async::Result;
|
||||||
use std::error;
|
|
||||||
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
fn main() -> Result<(), Box<error::Error>> {
|
#[tokio::main]
|
||||||
let mastodon = register::get_mastodon_data()?;
|
async fn main() -> Result<()> {
|
||||||
let tl = mastodon.get_home_timeline()?;
|
register::get_mastodon_data()
|
||||||
|
.await?
|
||||||
println!("{:#?}", tl);
|
.get_home_timeline()
|
||||||
|
.await?
|
||||||
|
.items_iter()
|
||||||
|
.for_each(|status| async move {
|
||||||
|
print!(
|
||||||
|
"\ttoot from {}:\n{}",
|
||||||
|
status.account.display_name,
|
||||||
|
html2text::parse(status.content.as_bytes())
|
||||||
|
.render_plain(90)
|
||||||
|
.into_string()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ use mastodon_async::Result;
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use mastodon_async::entities::prelude::Event;
|
|
||||||
|
|
||||||
femme::with_level(log::LevelFilter::Info);
|
femme::with_level(log::LevelFilter::Info);
|
||||||
let mastodon = register::get_mastodon_data().await?;
|
let mastodon = register::get_mastodon_data().await?;
|
||||||
|
|
|
@ -45,8 +45,8 @@ pub async fn register() -> Result<Mastodon> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
pub fn read_line(message: &str) -> Result<String> {
|
pub fn read_line(message: impl AsRef<str>) -> Result<String> {
|
||||||
println!("{}", message);
|
println!("{}", message.as_ref());
|
||||||
|
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
io::stdin().read_line(&mut input)?;
|
io::stdin().read_line(&mut input)?;
|
||||||
|
|
|
@ -1,15 +1,48 @@
|
||||||
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
#![cfg_attr(not(feature = "toml"), allow(dead_code))]
|
||||||
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
#![cfg_attr(not(feature = "toml"), allow(unused_imports))]
|
||||||
#[macro_use]
|
|
||||||
extern crate pretty_env_logger;
|
|
||||||
mod register;
|
mod register;
|
||||||
|
use mastodon_async::{Result, StatusBuilder, Visibility};
|
||||||
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
fn main() -> Result<(), Box<error::Error>> {
|
fn bool_input(message: impl AsRef<str>, default: bool) -> Result<bool> {
|
||||||
let mastodon = register::get_mastodon_data()?;
|
let input = register::read_line(message.as_ref())?;
|
||||||
|
if let Some(first_char) = input.chars().next() {
|
||||||
|
match first_char {
|
||||||
|
'Y' | 'y' => Ok(true),
|
||||||
|
'N' | 'n' => Ok(false),
|
||||||
|
'\n' => Ok(default),
|
||||||
|
_ => {
|
||||||
|
print!(
|
||||||
|
"I didn't understand '{input}'. Please input something that begins with 'y' \
|
||||||
|
or 'n', case insensitive: "
|
||||||
|
);
|
||||||
|
bool_input(message, default)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "toml")]
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
femme::with_level(femme::LevelFilter::Trace);
|
||||||
|
let mastodon = register::get_mastodon_data().await?;
|
||||||
let input = register::read_line("Enter the path to the photo you'd like to post: ")?;
|
let input = register::read_line("Enter the path to the photo you'd like to post: ")?;
|
||||||
|
|
||||||
mastodon.media(input.into())?;
|
let media = mastodon.media(input).await?;
|
||||||
|
let status = StatusBuilder::new()
|
||||||
|
.status("Mastodon-async photo upload example/demo (automated post)")
|
||||||
|
.media_ids([media.id])
|
||||||
|
.visibility(Visibility::Private)
|
||||||
|
.build()?;
|
||||||
|
let status = mastodon.new_status(status).await?;
|
||||||
|
println!("successfully uploaded status. It has the ID {}.", status.id);
|
||||||
|
if bool_input("would you like to delete the post now? (Y/n) ", true)? {
|
||||||
|
mastodon.delete_status(&status.id).await?;
|
||||||
|
println!("ok. done.");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub struct Attachment {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub media_type: MediaType,
|
pub media_type: MediaType,
|
||||||
/// URL of the locally hosted version of the image.
|
/// URL of the locally hosted version of the image.
|
||||||
pub url: String,
|
pub url: Option<String>,
|
||||||
/// For remote images, the remote URL of the original image.
|
/// For remote images, the remote URL of the original image.
|
||||||
pub remote_url: Option<String>,
|
pub remote_url: Option<String>,
|
||||||
/// URL of the preview image.
|
/// URL of the preview image.
|
||||||
|
|
|
@ -22,7 +22,12 @@ pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error from the Mastodon API. This typically means something went
|
/// Error from the Mastodon API. This typically means something went
|
||||||
/// wrong with your authentication or data.
|
/// wrong with your authentication or data.
|
||||||
Api(ApiError),
|
Api {
|
||||||
|
/// The response status.
|
||||||
|
status: StatusCode,
|
||||||
|
/// The JSON-decoded error response from the server.
|
||||||
|
response: ApiError,
|
||||||
|
},
|
||||||
/// Error deserialising to json. Typically represents a breaking change in
|
/// Error deserialising to json. Typically represents a breaking change in
|
||||||
/// the Mastodon API
|
/// the Mastodon API
|
||||||
Serde(SerdeError),
|
Serde(SerdeError),
|
||||||
|
@ -40,10 +45,6 @@ pub enum Error {
|
||||||
ClientSecretRequired,
|
ClientSecretRequired,
|
||||||
/// Missing Access Token.
|
/// Missing Access Token.
|
||||||
AccessTokenRequired,
|
AccessTokenRequired,
|
||||||
/// Generic client error.
|
|
||||||
Client(StatusCode),
|
|
||||||
/// Generic server error.
|
|
||||||
Server(StatusCode),
|
|
||||||
/// MastodonBuilder & AppBuilder error
|
/// MastodonBuilder & AppBuilder error
|
||||||
MissingField(&'static str),
|
MissingField(&'static str),
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
|
@ -79,39 +80,40 @@ impl fmt::Display for Error {
|
||||||
|
|
||||||
impl error::Error for Error {
|
impl error::Error for Error {
|
||||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
Some(match *self {
|
match *self {
|
||||||
Error::Api(ref e) => e,
|
Error::Serde(ref e) => Some(e),
|
||||||
Error::Serde(ref e) => e,
|
Error::UrlEncoded(ref e) => Some(e),
|
||||||
Error::UrlEncoded(ref e) => e,
|
Error::Http(ref e) => Some(e),
|
||||||
Error::Http(ref e) => e,
|
Error::Io(ref e) => Some(e),
|
||||||
Error::Io(ref e) => e,
|
Error::Url(ref e) => Some(e),
|
||||||
Error::Url(ref e) => e,
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
Error::TomlSer(ref e) => e,
|
Error::TomlSer(ref e) => Some(e),
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
Error::TomlDe(ref e) => e,
|
Error::TomlDe(ref e) => Some(e),
|
||||||
Error::HeaderStrError(ref e) => e,
|
Error::HeaderStrError(ref e) => Some(e),
|
||||||
Error::HeaderParseError(ref e) => e,
|
Error::HeaderParseError(ref e) => Some(e),
|
||||||
#[cfg(feature = "env")]
|
#[cfg(feature = "env")]
|
||||||
Error::Envy(ref e) => e,
|
Error::Envy(ref e) => Some(e),
|
||||||
Error::SerdeQs(ref e) => e,
|
Error::SerdeQs(ref e) => Some(e),
|
||||||
Error::IntConversion(ref e) => e,
|
Error::IntConversion(ref e) => Some(e),
|
||||||
Error::Client(..) | Error::Server(..) => return None,
|
Error::Api {
|
||||||
Error::ClientIdRequired => return None,
|
..
|
||||||
Error::ClientSecretRequired => return None,
|
}
|
||||||
Error::AccessTokenRequired => return None,
|
| Error::ClientIdRequired
|
||||||
Error::MissingField(_) => return None,
|
| Error::ClientSecretRequired
|
||||||
Error::Other(..) => return None,
|
| Error::AccessTokenRequired
|
||||||
})
|
| Error::MissingField(_)
|
||||||
|
| Error::Other(..) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error returned from the Mastodon API.
|
/// Error returned from the Mastodon API.
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct ApiError {
|
pub struct ApiError {
|
||||||
/// The type of error.
|
/// The error message.
|
||||||
pub error: Option<String>,
|
pub error: String,
|
||||||
/// The description of the error.
|
/// A longer description of the error, mainly provided with the OAuth API.
|
||||||
pub error_description: Option<String>,
|
pub error_description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +145,6 @@ from! {
|
||||||
SerdeError => Serde,
|
SerdeError => Serde,
|
||||||
UrlEncodedError => UrlEncoded,
|
UrlEncodedError => UrlEncoded,
|
||||||
UrlError => Url,
|
UrlError => Url,
|
||||||
ApiError => Api,
|
|
||||||
#[cfg(feature = "toml")] TomlSerError => TomlSer,
|
#[cfg(feature = "toml")] TomlSerError => TomlSer,
|
||||||
#[cfg(feature = "toml")] TomlDeError => TomlDe,
|
#[cfg(feature = "toml")] TomlDeError => TomlDe,
|
||||||
HeaderStrError => HeaderStrError,
|
HeaderStrError => HeaderStrError,
|
||||||
|
@ -213,16 +214,6 @@ mod tests {
|
||||||
assert_is!(err, Error::Url(..));
|
assert_is!(err, Error::Url(..));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn from_api_error() {
|
|
||||||
let err: ApiError = ApiError {
|
|
||||||
error: None,
|
|
||||||
error_description: None,
|
|
||||||
};
|
|
||||||
let err: Error = Error::from(err);
|
|
||||||
assert_is!(err, Error::Api(..));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
#[test]
|
#[test]
|
||||||
fn from_toml_ser_error() {
|
fn from_toml_ser_error() {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use envy;
|
use envy;
|
||||||
|
|
||||||
use crate::Result;
|
use crate::{Data, Result};
|
||||||
use data::Data;
|
|
||||||
|
|
||||||
/// Attempts to deserialize a Data struct from the environment
|
/// Attempts to deserialize a Data struct from the environment
|
||||||
pub fn from_env() -> Result<Data> {
|
pub fn from_env() -> Result<Data> {
|
||||||
|
|
|
@ -1,23 +1,27 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::errors::Result;
|
use crate::{errors::Result, log_serde, Error};
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use log::{as_serde, debug, trace, warn};
|
use log::{as_debug, as_serde, debug, trace, warn};
|
||||||
use reqwest::Response;
|
use reqwest::Response;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
|
||||||
/// Adapter for reading JSON data from a response with better logging and a
|
/// Adapter for reading JSON data from a response with better logging and a
|
||||||
/// fail-safe timeout.
|
/// fail-safe timeout.
|
||||||
|
///
|
||||||
|
/// The reason for this is largely because there was an issue with responses
|
||||||
|
/// being received, but not closed, we add a timeout on each read and try
|
||||||
|
/// to parse whatever we got before the timeout.
|
||||||
pub async fn read_response<T>(response: Response) -> Result<T>
|
pub async fn read_response<T>(response: Response) -> Result<T>
|
||||||
where
|
where
|
||||||
T: for<'de> Deserialize<'de> + Serialize,
|
T: for<'de> Deserialize<'de> + Serialize,
|
||||||
{
|
{
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
let url = response.url().clone();
|
let url = response.url().clone();
|
||||||
// let status = log_serde!(response Status);
|
let status = response.status();
|
||||||
// let headers = log_serde!(response Headers);
|
trace!(status = log_serde!(response Status), headers = log_serde!(response Headers); "attempting to stream response");
|
||||||
let stream = response.bytes_stream();
|
let stream = response.bytes_stream();
|
||||||
pin_mut!(stream);
|
pin_mut!(stream);
|
||||||
loop {
|
loop {
|
||||||
|
@ -35,23 +39,36 @@ where
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
url = url.as_str(), // status = status, headers = headers,
|
url = url.as_str(),
|
||||||
data_received = bytes.len();
|
data_received = bytes.len();
|
||||||
"API response timed out"
|
"API response timed out"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// done growing the vec, let's just do this once.
|
||||||
|
let bytes = bytes.as_slice();
|
||||||
trace!(
|
trace!(
|
||||||
url = url.as_str(), // status = status, headers = headers,
|
url = url.as_str(),
|
||||||
data_received = bytes.len();
|
data = String::from_utf8_lossy(bytes);
|
||||||
"parsing response"
|
"parsing response"
|
||||||
);
|
);
|
||||||
let result = serde_json::from_slice(bytes.as_slice())?;
|
if status.is_success() {
|
||||||
|
// the the response should deserialize to T
|
||||||
|
let result = serde_json::from_slice(bytes)?;
|
||||||
debug!(
|
debug!(
|
||||||
url = url.as_str(), // status = status, headers = headers,
|
url = url.as_str(),
|
||||||
result = as_serde!(result);
|
result = as_serde!(result);
|
||||||
"result parsed successfully"
|
"result parsed successfully"
|
||||||
);
|
);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
// we've received an error message, let's deserialize that instead.
|
||||||
|
let response = serde_json::from_slice(bytes)?;
|
||||||
|
debug!(status = as_debug!(status), response = as_serde!(response); "error received from API");
|
||||||
|
Err(Error::Api {
|
||||||
|
status,
|
||||||
|
response,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
158
src/macros.rs
158
src/macros.rs
|
@ -18,22 +18,12 @@ macro_rules! methods {
|
||||||
async fn $method_with_call_id<T: for<'de> serde::Deserialize<'de> + serde::Serialize>(&self, url: impl AsRef<str>, call_id: Uuid) -> Result<T>
|
async fn $method_with_call_id<T: for<'de> serde::Deserialize<'de> + serde::Serialize>(&self, url: impl AsRef<str>, call_id: Uuid) -> Result<T>
|
||||||
{
|
{
|
||||||
|
|
||||||
use log::{debug, error, as_debug, as_serde};
|
use log::{debug, as_debug};
|
||||||
|
|
||||||
let url = url.as_ref();
|
let url = url.as_ref();
|
||||||
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
||||||
let response = self.authenticated(self.client.$method(url)).send().await?;
|
let response = self.authenticated(self.client.$method(url)).header("Accept", "application/json").send().await?;
|
||||||
match response.error_for_status() {
|
read_response(response).await
|
||||||
Ok(response) => {
|
|
||||||
let response = read_response(response).await?;
|
|
||||||
debug!(response = as_serde!(response), url = url, method = stringify!($method), call_id = as_debug!(call_id); "received API response");
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
|
@ -57,22 +47,14 @@ macro_rules! paged_routes {
|
||||||
"```"
|
"```"
|
||||||
),
|
),
|
||||||
pub async fn $name(&self) -> Result<Page<$ret>> {
|
pub async fn $name(&self) -> Result<Page<$ret>> {
|
||||||
use log::{debug, as_debug, error};
|
use log::{debug, as_debug};
|
||||||
let url = self.route(concat!("/api/v1/", $url));
|
let url = self.route(concat!("/api/v1/", $url));
|
||||||
let call_id = uuid::Uuid::new_v4();
|
let call_id = uuid::Uuid::new_v4();
|
||||||
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
||||||
let response = self.authenticated(self.client.$method(&url)).send().await?;
|
let response = self.authenticated(self.client.$method(&url)).header("Accept", "application/json").send().await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
|
||||||
Ok(response) => {
|
|
||||||
Page::new(self.clone(), response, call_id).await
|
Page::new(self.clone(), response, call_id).await
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +70,7 @@ macro_rules! paged_routes {
|
||||||
),
|
),
|
||||||
pub async fn $name<'a>(&self, $($param: $typ,)*) -> Result<Page<$ret>> {
|
pub async fn $name<'a>(&self, $($param: $typ,)*) -> Result<Page<$ret>> {
|
||||||
use serde_urlencoded;
|
use serde_urlencoded;
|
||||||
use log::{debug, as_debug, error};
|
use log::{debug, as_debug};
|
||||||
|
|
||||||
let call_id = uuid::Uuid::new_v4();
|
let call_id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
@ -117,18 +99,10 @@ macro_rules! paged_routes {
|
||||||
|
|
||||||
debug!(url = url, method = "get", call_id = as_debug!(call_id); "making API request");
|
debug!(url = url, method = "get", call_id = as_debug!(call_id); "making API request");
|
||||||
|
|
||||||
let response = self.authenticated(self.client.get(&url)).send().await?;
|
let response = self.authenticated(self.client.get(&url)).header("Accept", "application/json").send().await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
|
||||||
Ok(response) => {
|
|
||||||
Page::new(self.clone(), response, call_id).await
|
Page::new(self.clone(), response, call_id).await
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paged_routes!{$($rest)*}
|
paged_routes!{$($rest)*}
|
||||||
|
@ -181,6 +155,62 @@ macro_rules! route_v2 {
|
||||||
route_v2!{$($rest)*}
|
route_v2!{$($rest)*}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
((post multipart ($($param:ident: $typ:ty,)*)) $name:ident: $url:expr => $ret:ty, $($rest:tt)*) => {
|
||||||
|
doc_comment! {
|
||||||
|
concat!(
|
||||||
|
"Equivalent to `post /api/v2/",
|
||||||
|
$url,
|
||||||
|
"`\n# Errors\nIf `access_token` is not set."),
|
||||||
|
pub async fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||||
|
use reqwest::multipart::{Form, Part};
|
||||||
|
use std::io::Read;
|
||||||
|
use log::{debug, error, as_debug};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
let call_id = Uuid::new_v4();
|
||||||
|
|
||||||
|
let form_data = Form::new()
|
||||||
|
$(
|
||||||
|
.part(stringify!($param), {
|
||||||
|
let path = $param.as_ref();
|
||||||
|
match std::fs::File::open(path) {
|
||||||
|
Ok(mut file) => {
|
||||||
|
let mut data = if let Ok(metadata) = file.metadata() {
|
||||||
|
Vec::with_capacity(metadata.len().try_into()?)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
file.read_to_end(&mut data)?;
|
||||||
|
Part::bytes(data)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(path = as_debug!(path), error = as_debug!(err); "error reading file contents for multipart form");
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)*;
|
||||||
|
|
||||||
|
let url = &self.route(concat!("/api/v2/", $url));
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
url = url, method = stringify!($method),
|
||||||
|
multipart_form_data = as_debug!(form_data), call_id = as_debug!(call_id);
|
||||||
|
"making API request"
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = self.authenticated(self.client.post(url))
|
||||||
|
.multipart(form_data)
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
read_response(response).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
route!{$($rest)*}
|
||||||
|
};
|
||||||
() => {}
|
() => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +225,7 @@ macro_rules! route {
|
||||||
pub async fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
pub async fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||||
use reqwest::multipart::{Form, Part};
|
use reqwest::multipart::{Form, Part};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use log::{debug, error, as_debug, as_serde};
|
use log::{debug, error, as_debug};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
let call_id = Uuid::new_v4();
|
let call_id = Uuid::new_v4();
|
||||||
|
@ -232,20 +262,11 @@ macro_rules! route {
|
||||||
|
|
||||||
let response = self.authenticated(self.client.post(url))
|
let response = self.authenticated(self.client.post(url))
|
||||||
.multipart(form_data)
|
.multipart(form_data)
|
||||||
|
.header("Accept", "application/json")
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
read_response(response).await
|
||||||
Ok(response) => {
|
|
||||||
let response = read_response(response).await?;
|
|
||||||
debug!(response = as_serde!(response), url = url, method = stringify!($method), call_id = as_debug!(call_id); "received API response");
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +325,7 @@ macro_rules! route {
|
||||||
"`\n# Errors\nIf `access_token` is not set.",
|
"`\n# Errors\nIf `access_token` is not set.",
|
||||||
),
|
),
|
||||||
pub async fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
pub async fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
|
||||||
use log::{debug, error, as_debug, as_serde};
|
use log::{debug, as_debug, as_serde};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
let call_id = Uuid::new_v4();
|
let call_id = Uuid::new_v4();
|
||||||
|
@ -324,20 +345,11 @@ macro_rules! route {
|
||||||
|
|
||||||
let response = self.authenticated(self.client.$method(url))
|
let response = self.authenticated(self.client.$method(url))
|
||||||
.json(&form_data)
|
.json(&form_data)
|
||||||
|
.header("Accept", "application/json")
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
read_response(response).await
|
||||||
Ok(response) => {
|
|
||||||
let response = read_response(response).await?;
|
|
||||||
debug!(response = as_serde!(response), url = $url, method = stringify!($method), call_id = as_debug!(call_id); "received API response");
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = $url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,24 +425,16 @@ macro_rules! paged_routes_with_id {
|
||||||
"```"
|
"```"
|
||||||
),
|
),
|
||||||
pub async fn $name(&self, id: &str) -> Result<Page<$ret>> {
|
pub async fn $name(&self, id: &str) -> Result<Page<$ret>> {
|
||||||
use log::{debug, error, as_debug};
|
use log::{debug, as_debug};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
let call_id = Uuid::new_v4();
|
let call_id = Uuid::new_v4();
|
||||||
let url = self.route(&format!(concat!("/api/v1/", $url), id));
|
let url = self.route(&format!(concat!("/api/v1/", $url), id));
|
||||||
|
|
||||||
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
||||||
let response = self.authenticated(self.client.$method(&url)).send().await?;
|
let response = self.authenticated(self.client.$method(&url)).header("Accept", "application/json").send().await?;
|
||||||
match response.error_for_status() {
|
|
||||||
Ok(response) => {
|
|
||||||
Page::new(self.clone(), response, call_id).await
|
Page::new(self.clone(), response, call_id).await
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paged_routes_with_id!{$($rest)*}
|
paged_routes_with_id!{$($rest)*}
|
||||||
|
@ -469,13 +473,19 @@ tokio_test::block_on(async {
|
||||||
),
|
),
|
||||||
pub async fn $fn_name(&self) -> Result<impl TryStream<Ok=Event, Error=Error>> {
|
pub async fn $fn_name(&self) -> Result<impl TryStream<Ok=Event, Error=Error>> {
|
||||||
let url = self.route(&format!("/api/v1/streaming/{}", $stream));
|
let url = self.route(&format!("/api/v1/streaming/{}", $stream));
|
||||||
let response = self.authenticated(self.client.get(&url)).send().await?;
|
let response = self.authenticated(self.client.get(&url)).header("Accept", "application/json").send().await?;
|
||||||
debug!(
|
debug!(
|
||||||
status = log_serde!(response Status), url = &url,
|
status = log_serde!(response Status), url = &url,
|
||||||
headers = log_serde!(response Headers);
|
headers = log_serde!(response Headers);
|
||||||
"received API response"
|
"received API response"
|
||||||
);
|
);
|
||||||
Ok(event_stream(response.error_for_status()?, url))
|
let status = response.status();
|
||||||
|
if status.is_success() {
|
||||||
|
Ok(event_stream(response, url))
|
||||||
|
} else {
|
||||||
|
let response = response.json().await?;
|
||||||
|
Err(Error::Api{ status, response })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
streaming! { $($rest)* }
|
streaming! { $($rest)* }
|
||||||
|
@ -513,13 +523,19 @@ tokio_test::block_on(async {
|
||||||
let mut url: Url = self.route(concat!("/api/v1/streaming/", stringify!($stream))).parse()?;
|
let mut url: Url = self.route(concat!("/api/v1/streaming/", stringify!($stream))).parse()?;
|
||||||
url.query_pairs_mut().append_pair(stringify!($param), $param.as_ref());
|
url.query_pairs_mut().append_pair(stringify!($param), $param.as_ref());
|
||||||
let url = url.to_string();
|
let url = url.to_string();
|
||||||
let response = self.authenticated(self.client.get(url.as_str())).send().await?;
|
let response = self.authenticated(self.client.get(url.as_str())).header("Accept", "application/json").send().await?;
|
||||||
debug!(
|
debug!(
|
||||||
status = log_serde!(response Status), url = as_debug!(url),
|
status = log_serde!(response Status), url = as_debug!(url),
|
||||||
headers = log_serde!(response Headers);
|
headers = log_serde!(response Headers);
|
||||||
"received API response"
|
"received API response"
|
||||||
);
|
);
|
||||||
Ok(event_stream(response.error_for_status()?, url))
|
let status = response.status();
|
||||||
|
if status.is_success() {
|
||||||
|
Ok(event_stream(response, url))
|
||||||
|
} else {
|
||||||
|
let response = response.json().await?;
|
||||||
|
Err(Error::Api{ status, response })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
streaming! { $($rest)* }
|
streaming! { $($rest)* }
|
||||||
|
|
|
@ -91,7 +91,6 @@ impl Mastodon {
|
||||||
(post (id: &str,)) reject_follow_request: "accounts/follow_requests/reject" => Empty,
|
(post (id: &str,)) reject_follow_request: "accounts/follow_requests/reject" => Empty,
|
||||||
(get (local: bool,)) get_public_timeline: "timelines/public" => Vec<Status>,
|
(get (local: bool,)) get_public_timeline: "timelines/public" => Vec<Status>,
|
||||||
(post (uri: Cow<'static, str>,)) follows: "follows" => Account,
|
(post (uri: Cow<'static, str>,)) follows: "follows" => Account,
|
||||||
(post multipart (file: impl AsRef<Path>,)) media: "media" => Attachment,
|
|
||||||
(post) clear_notifications: "notifications/clear" => Empty,
|
(post) clear_notifications: "notifications/clear" => Empty,
|
||||||
(post (id: &str,)) dismiss_notification: "notifications/dismiss" => Empty,
|
(post (id: &str,)) dismiss_notification: "notifications/dismiss" => Empty,
|
||||||
(get) get_push_subscription: "push/subscription" => Subscription,
|
(get) get_push_subscription: "push/subscription" => Subscription,
|
||||||
|
@ -102,6 +101,8 @@ impl Mastodon {
|
||||||
|
|
||||||
route_v2! {
|
route_v2! {
|
||||||
(get (q: &'a str, resolve: bool,)) search: "search" => SearchResult,
|
(get (q: &'a str, resolve: bool,)) search: "search" => SearchResult,
|
||||||
|
(post multipart (file: impl AsRef<Path>,)) media: "media" => Attachment,
|
||||||
|
(post multipart (file: impl AsRef<Path>, thumbnail: impl AsRef<Path>,)) media_with_thumbnail: "media" => Attachment,
|
||||||
}
|
}
|
||||||
|
|
||||||
route_id! {
|
route_id! {
|
||||||
|
@ -169,14 +170,14 @@ impl Mastodon {
|
||||||
|
|
||||||
/// POST /api/v1/filters
|
/// POST /api/v1/filters
|
||||||
pub async fn add_filter(&self, request: &mut AddFilterRequest) -> Result<Filter> {
|
pub async fn add_filter(&self, request: &mut AddFilterRequest) -> Result<Filter> {
|
||||||
Ok(self
|
let response = self
|
||||||
.client
|
.client
|
||||||
.post(self.route("/api/v1/filters"))
|
.post(self.route("/api/v1/filters"))
|
||||||
.json(&request)
|
.json(&request)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?;
|
||||||
.json()
|
|
||||||
.await?)
|
read_response(response).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT /api/v1/filters/:id
|
/// PUT /api/v1/filters/:id
|
||||||
|
@ -184,15 +185,7 @@ impl Mastodon {
|
||||||
let url = self.route(&format!("/api/v1/filters/{}", id));
|
let url = self.route(&format!("/api/v1/filters/{}", id));
|
||||||
let response = self.client.put(&url).json(&request).send().await?;
|
let response = self.client.put(&url).json(&request).send().await?;
|
||||||
|
|
||||||
let status = response.status();
|
read_response(response).await
|
||||||
|
|
||||||
if status.is_client_error() {
|
|
||||||
return Err(Error::Client(status.clone()));
|
|
||||||
} else if status.is_server_error() {
|
|
||||||
return Err(Error::Server(status.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(read_response(response).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the user credentials
|
/// Update the user credentials
|
||||||
|
@ -201,15 +194,7 @@ impl Mastodon {
|
||||||
let url = self.route("/api/v1/accounts/update_credentials");
|
let url = self.route("/api/v1/accounts/update_credentials");
|
||||||
let response = self.client.patch(&url).json(&changes).send().await?;
|
let response = self.client.patch(&url).json(&changes).send().await?;
|
||||||
|
|
||||||
let status = response.status();
|
read_response(response).await
|
||||||
|
|
||||||
if status.is_client_error() {
|
|
||||||
return Err(Error::Client(status.clone()));
|
|
||||||
} else if status.is_server_error() {
|
|
||||||
return Err(Error::Server(status.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(read_response(response).await?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Post a new status to the account.
|
/// Post a new status to the account.
|
||||||
|
@ -225,7 +210,7 @@ impl Mastodon {
|
||||||
headers = log_serde!(response Headers);
|
headers = log_serde!(response Headers);
|
||||||
"received API response"
|
"received API response"
|
||||||
);
|
);
|
||||||
Ok(read_response(response).await?)
|
read_response(response).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get timeline filtered by a hashtag(eg. `#coffee`) either locally or
|
/// Get timeline filtered by a hashtag(eg. `#coffee`) either locally or
|
||||||
|
@ -279,15 +264,7 @@ impl Mastodon {
|
||||||
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
debug!(url = url, method = stringify!($method), call_id = as_debug!(call_id); "making API request");
|
||||||
let response = self.client.get(&url).send().await?;
|
let response = self.client.get(&url).send().await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
Page::new(self.clone(), response, call_id).await
|
||||||
Ok(response) => Page::new(self.clone(), response, call_id).await,
|
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
// Cannot retrieve request body as it's been moved into the
|
|
||||||
// other match arm.
|
|
||||||
Err(err.into())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the client account's relationship to a list of other accounts.
|
/// Returns the client account's relationship to a list of other accounts.
|
||||||
|
@ -315,18 +292,7 @@ impl Mastodon {
|
||||||
);
|
);
|
||||||
let response = self.client.get(&url).send().await?;
|
let response = self.client.get(&url).send().await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
Page::new(self.clone(), response, call_id).await
|
||||||
Ok(response) => Page::new(self.clone(), response, call_id).await,
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
err = as_debug!(err), url = url,
|
|
||||||
method = stringify!($method), call_id = as_debug!(call_id),
|
|
||||||
account_ids = as_serde!(ids);
|
|
||||||
"error making API request"
|
|
||||||
);
|
|
||||||
Err(err.into())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a push notifications subscription
|
/// Add a push notifications subscription
|
||||||
|
@ -341,18 +307,7 @@ impl Mastodon {
|
||||||
);
|
);
|
||||||
let response = self.client.post(url).json(&request).send().await?;
|
let response = self.client.post(url).json(&request).send().await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
read_response(response).await
|
||||||
Ok(response) => {
|
|
||||||
let status = response.status();
|
|
||||||
let response = read_response(response).await?;
|
|
||||||
debug!(status = as_debug!(status), response = as_serde!(response); "received API response");
|
|
||||||
Ok(response)
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the `data` portion of the push subscription associated with this
|
/// Update the `data` portion of the push subscription associated with this
|
||||||
|
@ -368,18 +323,7 @@ impl Mastodon {
|
||||||
);
|
);
|
||||||
let response = self.client.post(url).json(&request).send().await?;
|
let response = self.client.post(url).json(&request).send().await?;
|
||||||
|
|
||||||
match response.error_for_status() {
|
read_response(response).await
|
||||||
Ok(response) => {
|
|
||||||
let status = response.status();
|
|
||||||
let response = read_response(response).await?;
|
|
||||||
debug!(status = as_debug!(status), response = as_serde!(response); "received API response");
|
|
||||||
Ok(response)
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
error!(err = as_debug!(err), url = url, method = stringify!($method), call_id = as_debug!(call_id); "error making API request");
|
|
||||||
Err(err.into())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all accounts that follow the authenticated user
|
/// Get all accounts that follow the authenticated user
|
||||||
|
|
16
src/page.rs
16
src/page.rs
|
@ -1,5 +1,10 @@
|
||||||
use super::{Mastodon, Result};
|
use super::{Mastodon, Result};
|
||||||
use crate::{entities::itemsiter::ItemsIter, format_err, helpers::read_response::read_response};
|
use crate::{
|
||||||
|
entities::itemsiter::ItemsIter,
|
||||||
|
format_err,
|
||||||
|
helpers::read_response::read_response,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use hyper_old_types::header::{parsing, Link, RelationType};
|
use hyper_old_types::header::{parsing, Link, RelationType};
|
||||||
use log::{as_debug, as_serde, debug, error, trace};
|
use log::{as_debug, as_serde, debug, error, trace};
|
||||||
|
@ -107,6 +112,8 @@ impl<'a, T: for<'de> Deserialize<'de> + Serialize> Page<T> {
|
||||||
|
|
||||||
/// Create a new Page.
|
/// Create a new Page.
|
||||||
pub(crate) async fn new(mastodon: Mastodon, response: Response, call_id: Uuid) -> Result<Self> {
|
pub(crate) async fn new(mastodon: Mastodon, response: Response, call_id: Uuid) -> Result<Self> {
|
||||||
|
let status = response.status();
|
||||||
|
if status.is_success() {
|
||||||
let (prev, next) = get_links(&response, call_id)?;
|
let (prev, next) = get_links(&response, call_id)?;
|
||||||
let initial_items = read_response(response).await?;
|
let initial_items = read_response(response).await?;
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -121,6 +128,13 @@ impl<'a, T: for<'de> Deserialize<'de> + Serialize> Page<T> {
|
||||||
mastodon,
|
mastodon,
|
||||||
call_id,
|
call_id,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
let response = response.json().await?;
|
||||||
|
Err(Error::Api {
|
||||||
|
status,
|
||||||
|
response,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue