Version 0.6

Added proper error handling
impl fmt::Display for apps::Scope
make StatusBuilder.status public
master
Aaron Power 8 years ago
parent 52f8d5e500
commit 39e539646b
  1. 2
      Cargo.toml
  2. 15
      src/apps.rs
  3. 123
      src/lib.rs
  4. 8
      src/registration.rs
  5. 2
      src/status_builder.rs

@ -1,6 +1,6 @@
[package] [package]
name = "mammut" name = "mammut"
version = "0.5.2" version = "0.6.0"
description = "A wrapper around the Mastodon API." description = "A wrapper around the Mastodon API."
authors = ["Aaron Power <theaaronepower@gmail.com>"] authors = ["Aaron Power <theaaronepower@gmail.com>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"

@ -37,6 +37,21 @@ pub enum Scope {
WriteFollow, WriteFollow,
} }
impl ::std::fmt::Display for Scope {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use self::Scope::*;
write!(f, "{}", match *self {
All => "read write follow",
Follow => "follow",
Read => "read",
ReadFollow => "read follow",
ReadWrite => "read write",
Write => "write",
WriteFollow => "write follow"
})
}
}
impl Default for Scope { impl Default for Scope {
fn default() -> Self { fn default() -> Self {
Scope::Read Scope::Read

@ -50,6 +50,7 @@ pub mod entities;
pub mod registration; pub mod registration;
use std::ops; use std::ops;
use std::io::Error as IoError;
use json::Error as SerdeError; use json::Error as SerdeError;
use reqwest::Error as HttpError; use reqwest::Error as HttpError;
@ -57,7 +58,7 @@ use reqwest::Client;
use reqwest::header::{Authorization, Bearer, Headers}; use reqwest::header::{Authorization, Bearer, Headers};
use entities::prelude::*; use entities::prelude::*;
use status_builder::StatusBuilder; pub use status_builder::StatusBuilder;
pub use registration::Registration; pub use registration::Registration;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@ -72,11 +73,7 @@ macro_rules! methods {
.headers(self.headers.clone()) .headers(self.headers.clone())
.send()?; .send()?;
if let Ok(t) = response.json::<T>() { response.json()?
Ok(t)
} else {
Err(Error::Api(response.json()?))
}
} }
)+ )+
}; };
@ -92,23 +89,24 @@ macro_rules! route {
#[doc = "# Errors"] #[doc = "# Errors"]
/// If `access_token` is not set. /// If `access_token` is not set.
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> { pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
use std::io::Read;
let form_data = json!({ let form_data = json!({
$( $(
stringify!($param): $param, stringify!($param): $param,
)* )*
}); });
let mut response = self.client.post(&self.route(concat!("/api/v1/", $url))) let mut response = self.client.post(&self.route(concat!("/api/v1/", $url)))
.headers(self.headers.clone()) .headers(self.headers.clone())
.form(&form_data) .form(&form_data)
.send()?; .send()?;
if let Ok(t) = response.json::<$ret>() { let mut vec = Vec::new();
Ok(t)
} else { response.read_to_end(&mut vec)?;
Err(Error::Api(response.json()?))
} json::from_slice(&vec)?
} }
route!{$($rest)*} route!{$($rest)*}
}; };
@ -167,13 +165,21 @@ pub struct Data {
pub token: String, pub token: String,
} }
#[derive(Debug)] #[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum Error { pub enum Error {
Api(ApiError), Api(ApiError),
#[serde(skip_deserializing)]
Serde(SerdeError), Serde(SerdeError),
#[serde(skip_deserializing)]
Http(HttpError), Http(HttpError),
#[serde(skip_deserializing)]
Io(IoError),
#[serde(skip_deserializing)]
ClientIdRequired, ClientIdRequired,
#[serde(skip_deserializing)]
ClientSecretRequired, ClientSecretRequired,
#[serde(skip_deserializing)]
AccessTokenRequired, AccessTokenRequired,
} }
@ -183,7 +189,7 @@ pub struct ApiError {
/// The type of error. /// The type of error.
pub error: String, pub error: String,
/// The description of the error. /// The description of the error.
pub error_description: String, pub error_description: Option<String>,
} }
impl Mastodon { impl Mastodon {
@ -194,25 +200,25 @@ impl Mastodon {
token: String, token: String,
client: Client) client: Client)
-> Self -> Self
{ {
let data = Data { let data = Data {
base: base, base: base,
client_id: client_id, client_id: client_id,
client_secret: client_secret, client_secret: client_secret,
redirect: redirect, redirect: redirect,
token: token, token: token,
}; };
let mut headers = Headers::new(); let mut headers = Headers::new();
headers.set(Authorization(Bearer { token: data.token.clone() })); headers.set(Authorization(Bearer { token: data.token.clone() }));
Mastodon { Mastodon {
client: client, client: client,
headers: headers, headers: headers,
data: data, data: data,
}
} }
}
/// Creates a mastodon instance from the data struct. /// Creates a mastodon instance from the data struct.
pub fn from_data(data: Data) -> Result<Self> { pub fn from_data(data: Data) -> Result<Self> {
@ -242,7 +248,6 @@ impl Mastodon {
(post (account_id: u64, status_ids: Vec<u64>, comment: String,)) report: (post (account_id: u64, status_ids: Vec<u64>, comment: String,)) report:
"reports" => Report, "reports" => Report,
(post (q: String, resolve: bool,)) search: "search" => SearchResult, (post (q: String, resolve: bool,)) search: "search" => SearchResult,
(post (status: StatusBuilder,)) new_status: "statuses" => Status,
} }
route_id! { route_id! {
@ -268,6 +273,25 @@ impl Mastodon {
(delete) delete_status: "statuses/{}" => Empty, (delete) delete_status: "statuses/{}" => Empty,
} }
pub fn new_status(&self, status: StatusBuilder) -> Result<Status> {
use std::io::Read;
let mut response = self.client.post(&self.route("/api/v1/statuses"))
.headers(self.headers.clone())
.json(&status)
.send().expect("STAUS BUILDER IS BAD");
let mut vec = Vec::new();
response.read_to_end(&mut vec)?;
if let Ok(t) = json::from_slice(&vec) {
Ok(t)
} else {
Err(Error::Api(json::from_slice(&vec)?))
}
}
pub fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> { pub fn get_public_timeline(&self, local: bool) -> Result<Vec<Status>> {
let mut url = self.route("/api/v1/timelines/public"); let mut url = self.route("/api/v1/timelines/public");
@ -291,25 +315,25 @@ impl Mastodon {
pub fn statuses(&self, id: u64, only_media: bool, exclude_replies: bool) pub fn statuses(&self, id: u64, only_media: bool, exclude_replies: bool)
-> Result<Vec<Status>> -> Result<Vec<Status>>
{ {
let mut url = format!("{}/api/v1/accounts/{}/statuses", self.base, id); let mut url = format!("{}/api/v1/accounts/{}/statuses", self.base, id);
if only_media { if only_media {
url += "?only_media=1"; url += "?only_media=1";
} }
if exclude_replies { if exclude_replies {
url += if only_media { url += if only_media {
"&" "&"
} else { } else {
"?" "?"
}; };
url += "exclude_replies=1"; url += "exclude_replies=1";
} }
self.get(url) self.get(url)
} }
pub fn relationships(&self, ids: &[u64]) -> Result<Vec<Relationship>> { pub fn relationships(&self, ids: &[u64]) -> Result<Vec<Relationship>> {
@ -372,4 +396,5 @@ macro_rules! from {
from! { from! {
SerdeError, Serde, SerdeError, Serde,
HttpError, Http, HttpError, Http,
IoError, Io,
} }

@ -1,7 +1,7 @@
use reqwest::Client; use reqwest::Client;
use super::{Error, Mastodon, Result}; use super::{Error, Mastodon, Result};
use apps::AppBuilder; use apps::{AppBuilder, Scope};
/// 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.
@ -11,6 +11,7 @@ pub struct Registration {
client_id: Option<String>, client_id: Option<String>,
client_secret: Option<String>, client_secret: Option<String>,
redirect: Option<String>, redirect: Option<String>,
scopes: Scope,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -33,6 +34,7 @@ impl Registration {
client_id: None, client_id: None,
client_secret: None, client_secret: None,
redirect: None, redirect: None,
scopes: Scope::Read,
}) })
} }
@ -68,6 +70,7 @@ impl Registration {
/// ``` /// ```
pub fn register(&mut self, app_builder: AppBuilder) -> Result<()> { pub fn register(&mut self, app_builder: AppBuilder) -> Result<()> {
let url = format!("{}/api/v1/apps", self.base); let url = format!("{}/api/v1/apps", self.base);
self.scopes = app_builder.scopes;
let app: OAuth = self.client.post(&url).form(&app_builder).send()?.json()?; let app: OAuth = self.client.post(&url).form(&app_builder).send()?.json()?;
@ -84,10 +87,11 @@ impl Registration {
self.is_registered()?; self.is_registered()?;
let url = format!( let url = format!(
"{}/oauth/authorize?client_id={}&redirect_uri={}&response_type=code", "{}/oauth/authorize?client_id={}&redirect_uri={}&scope={}&response_type=code",
self.base, self.base,
self.client_id.clone().unwrap(), self.client_id.clone().unwrap(),
self.redirect.clone().unwrap(), self.redirect.clone().unwrap(),
self.scopes,
); );
Ok(url) Ok(url)

@ -1,6 +1,6 @@
#[derive(Debug, Default, Clone, Serialize)] #[derive(Debug, Default, Clone, Serialize)]
pub struct StatusBuilder { pub struct StatusBuilder {
status: String, pub status: String,
/// User ids of those to reply to. /// User ids of those to reply to.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub in_reply_to_id: Option<u64>, pub in_reply_to_id: Option<u64>,

Loading…
Cancel
Save