diff --git a/Cargo.toml b/Cargo.toml index af16166..2e6c4b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mammut" -version = "0.5.2" +version = "0.6.0" description = "A wrapper around the Mastodon API." authors = ["Aaron Power "] license = "MIT/Apache-2.0" diff --git a/src/apps.rs b/src/apps.rs index 787e770..b0bccd2 100644 --- a/src/apps.rs +++ b/src/apps.rs @@ -37,6 +37,21 @@ pub enum Scope { 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 { fn default() -> Self { Scope::Read diff --git a/src/lib.rs b/src/lib.rs index a37cb48..eb98d84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ pub mod entities; pub mod registration; use std::ops; +use std::io::Error as IoError; use json::Error as SerdeError; use reqwest::Error as HttpError; @@ -57,7 +58,7 @@ use reqwest::Client; use reqwest::header::{Authorization, Bearer, Headers}; use entities::prelude::*; -use status_builder::StatusBuilder; +pub use status_builder::StatusBuilder; pub use registration::Registration; pub type Result = std::result::Result; @@ -72,11 +73,7 @@ macro_rules! methods { .headers(self.headers.clone()) .send()?; - if let Ok(t) = response.json::() { - Ok(t) - } else { - Err(Error::Api(response.json()?)) - } + response.json()? } )+ }; @@ -92,23 +89,24 @@ macro_rules! route { #[doc = "# Errors"] /// If `access_token` is not set. pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> { + use std::io::Read; let form_data = json!({ $( stringify!($param): $param, - )* + )* }); let mut response = self.client.post(&self.route(concat!("/api/v1/", $url))) - .headers(self.headers.clone()) - .form(&form_data) - .send()?; - - if let Ok(t) = response.json::<$ret>() { - Ok(t) - } else { - Err(Error::Api(response.json()?)) - } + .headers(self.headers.clone()) + .form(&form_data) + .send()?; + + let mut vec = Vec::new(); + + response.read_to_end(&mut vec)?; + + json::from_slice(&vec)? } route!{$($rest)*} }; @@ -167,13 +165,21 @@ pub struct Data { pub token: String, } -#[derive(Debug)] +#[derive(Debug, Deserialize)] +#[serde(untagged)] pub enum Error { Api(ApiError), + #[serde(skip_deserializing)] Serde(SerdeError), + #[serde(skip_deserializing)] Http(HttpError), + #[serde(skip_deserializing)] + Io(IoError), + #[serde(skip_deserializing)] ClientIdRequired, + #[serde(skip_deserializing)] ClientSecretRequired, + #[serde(skip_deserializing)] AccessTokenRequired, } @@ -183,7 +189,7 @@ pub struct ApiError { /// The type of error. pub error: String, /// The description of the error. - pub error_description: String, + pub error_description: Option, } impl Mastodon { @@ -194,25 +200,25 @@ impl Mastodon { token: String, client: Client) -> Self - { - let data = Data { - base: base, - client_id: client_id, - client_secret: client_secret, - redirect: redirect, - token: token, + { + let data = Data { + base: base, + client_id: client_id, + client_secret: client_secret, + redirect: redirect, + token: token, - }; + }; - let mut headers = Headers::new(); - headers.set(Authorization(Bearer { token: data.token.clone() })); + let mut headers = Headers::new(); + headers.set(Authorization(Bearer { token: data.token.clone() })); - Mastodon { - client: client, - headers: headers, - data: data, + Mastodon { + client: client, + headers: headers, + data: data, + } } - } /// Creates a mastodon instance from the data struct. pub fn from_data(data: Data) -> Result { @@ -242,7 +248,6 @@ impl Mastodon { (post (account_id: u64, status_ids: Vec, comment: String,)) report: "reports" => Report, (post (q: String, resolve: bool,)) search: "search" => SearchResult, - (post (status: StatusBuilder,)) new_status: "statuses" => Status, } route_id! { @@ -268,6 +273,25 @@ impl Mastodon { (delete) delete_status: "statuses/{}" => Empty, } + pub fn new_status(&self, status: StatusBuilder) -> Result { + 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> { 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) -> Result> - { - 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 { - url += "?only_media=1"; - } + if only_media { + url += "?only_media=1"; + } - if exclude_replies { - url += if only_media { - "&" - } else { - "?" - }; + if exclude_replies { + url += if only_media { + "&" + } else { + "?" + }; - url += "exclude_replies=1"; - } + url += "exclude_replies=1"; + } - self.get(url) - } + self.get(url) + } pub fn relationships(&self, ids: &[u64]) -> Result> { @@ -372,4 +396,5 @@ macro_rules! from { from! { SerdeError, Serde, HttpError, Http, + IoError, Io, } diff --git a/src/registration.rs b/src/registration.rs index 8b58f57..d725640 100644 --- a/src/registration.rs +++ b/src/registration.rs @@ -1,7 +1,7 @@ use reqwest::Client; use super::{Error, Mastodon, Result}; -use apps::AppBuilder; +use apps::{AppBuilder, Scope}; /// Handles registering your mastodon app to your instance. It is recommended /// you cache your data struct to avoid registering on every run. @@ -11,6 +11,7 @@ pub struct Registration { client_id: Option, client_secret: Option, redirect: Option, + scopes: Scope, } #[derive(Deserialize)] @@ -33,6 +34,7 @@ impl Registration { client_id: None, client_secret: None, redirect: None, + scopes: Scope::Read, }) } @@ -68,6 +70,7 @@ impl Registration { /// ``` pub fn register(&mut self, app_builder: AppBuilder) -> Result<()> { 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()?; @@ -84,10 +87,11 @@ impl Registration { self.is_registered()?; 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.client_id.clone().unwrap(), self.redirect.clone().unwrap(), + self.scopes, ); Ok(url) diff --git a/src/status_builder.rs b/src/status_builder.rs index aafd9a6..e733d0d 100644 --- a/src/status_builder.rs +++ b/src/status_builder.rs @@ -1,6 +1,6 @@ #[derive(Debug, Default, Clone, Serialize)] pub struct StatusBuilder { - status: String, + pub status: String, /// User ids of those to reply to. #[serde(skip_serializing_if="Option::is_none")] pub in_reply_to_id: Option,