From 16bc060407fc23660994bf3f4894cfecf2a46148 Mon Sep 17 00:00:00 2001 From: Paul Woolcock Date: Tue, 9 Jun 2020 09:11:18 -0400 Subject: Update to the 2018 edition Only 2 years later :eyeroll: --- Cargo.toml | 1 + examples/follow_profile.rs | 4 +- examples/follows_me.rs | 4 +- examples/home_timeline.rs | 4 +- examples/print_your_profile.rs | 4 +- examples/register/mod.rs | 8 +- examples/search.rs | 4 +- examples/upload_photo.rs | 4 +- macro-dbg | 30158 +++++++++++++++++++++++++++++++++++ src/apps.rs | 4 +- src/entities/account.rs | 2 +- src/entities/event.rs | 2 +- src/entities/itemsiter.rs | 4 +- src/entities/status.rs | 4 +- src/errors.rs | 8 +- src/helpers/cli.rs | 8 +- src/helpers/env.rs | 4 +- src/helpers/json.rs | 4 +- src/helpers/toml.rs | 6 +- src/http_send.rs | 2 +- src/lib.rs | 38 +- src/mastodon_client.rs | 12 +- src/page.rs | 4 +- src/registration.rs | 16 +- src/requests/filter.rs | 2 +- src/requests/push.rs | 10 +- src/requests/statuses.rs | 2 +- src/requests/update_credentials.rs | 10 +- src/scopes.rs | 2 +- 29 files changed, 30247 insertions(+), 88 deletions(-) create mode 100644 macro-dbg diff --git a/Cargo.toml b/Cargo.toml index 2212631..b4d758e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ readme = "README.md" repository = "https://github.com/pwoolcoc/elefren.git" keywords = ["api", "web", "social", "mastodon", "wrapper"] categories = ["web-programming", "web-programming::http-client", "api-bindings"] +edition = "2018" [dependencies] doc-comment = "0.3" diff --git a/examples/follow_profile.rs b/examples/follow_profile.rs index 84b2998..2d97380 100644 --- a/examples/follow_profile.rs +++ b/examples/follow_profile.rs @@ -5,11 +5,11 @@ extern crate pretty_env_logger; extern crate elefren; mod register; -use register::MastodonClient; +use crate::register::MastodonClient; use std::error; #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let mastodon = register::get_mastodon_data()?; let input = register::read_line("Enter the account id you'd like to follow: ")?; let new_follow = mastodon.follow(input.trim())?; diff --git a/examples/follows_me.rs b/examples/follows_me.rs index 0df4414..2008fb5 100644 --- a/examples/follows_me.rs +++ b/examples/follows_me.rs @@ -5,11 +5,11 @@ extern crate pretty_env_logger; extern crate elefren; mod register; -use register::MastodonClient; +use crate::register::MastodonClient; use std::error; #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let mastodon = register::get_mastodon_data()?; for account in mastodon.follows_me()?.items_iter() { println!("{}", account.acct); diff --git a/examples/home_timeline.rs b/examples/home_timeline.rs index 46519e3..0f3c920 100644 --- a/examples/home_timeline.rs +++ b/examples/home_timeline.rs @@ -5,11 +5,11 @@ extern crate pretty_env_logger; extern crate elefren; mod register; -use register::MastodonClient; +use crate::register::MastodonClient; use std::error; #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let mastodon = register::get_mastodon_data()?; let tl = mastodon.get_home_timeline()?; diff --git a/examples/print_your_profile.rs b/examples/print_your_profile.rs index cbcd372..126110a 100644 --- a/examples/print_your_profile.rs +++ b/examples/print_your_profile.rs @@ -5,11 +5,11 @@ extern crate pretty_env_logger; extern crate elefren; mod register; -use register::MastodonClient; +use crate::register::MastodonClient; use std::error; #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let mastodon = register::get_mastodon_data()?; let you = mastodon.verify_credentials()?; diff --git a/examples/register/mod.rs b/examples/register/mod.rs index d8dbaff..1c429b5 100644 --- a/examples/register/mod.rs +++ b/examples/register/mod.rs @@ -11,14 +11,14 @@ use elefren::helpers::toml; #[allow(dead_code)] #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { register()?; Ok(()) } #[allow(dead_code)] #[cfg(feature = "toml")] -pub fn get_mastodon_data() -> Result> { +pub fn get_mastodon_data() -> Result> { if let Ok(data) = toml::from_file("mastodon-data.toml") { Ok(Mastodon::from(data)) } else { @@ -27,7 +27,7 @@ pub fn get_mastodon_data() -> Result> { } #[cfg(feature = "toml")] -pub fn register() -> Result> { +pub fn register() -> Result> { let website = read_line("Please enter your mastodon instance url:")?; let registration = Registration::new(website.trim()) .client_name("elefren-examples") @@ -43,7 +43,7 @@ pub fn register() -> Result> { } #[cfg(feature = "toml")] -pub fn read_line(message: &str) -> Result> { +pub fn read_line(message: &str) -> Result> { println!("{}", message); let mut input = String::new(); diff --git a/examples/search.rs b/examples/search.rs index ec13f6d..c619a74 100644 --- a/examples/search.rs +++ b/examples/search.rs @@ -5,11 +5,11 @@ extern crate pretty_env_logger; extern crate elefren; mod register; -use register::MastodonClient; +use crate::register::MastodonClient; use std::error; #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let mastodon = register::get_mastodon_data()?; let input = register::read_line("Enter the term you'd like to search: ")?; let result = mastodon.search(&input, false)?; diff --git a/examples/upload_photo.rs b/examples/upload_photo.rs index a2e6688..fa2dee9 100644 --- a/examples/upload_photo.rs +++ b/examples/upload_photo.rs @@ -5,11 +5,11 @@ extern crate pretty_env_logger; extern crate elefren; mod register; -use register::MastodonClient; +use crate::register::MastodonClient; use std::error; #[cfg(feature = "toml")] -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let mastodon = register::get_mastodon_data()?; let input = register::read_line("Enter the path to the photo you'd like to post: ")?; diff --git a/macro-dbg b/macro-dbg new file mode 100644 index 0000000..27be5c5 --- /dev/null +++ b/macro-dbg @@ -0,0 +1,30158 @@ +#![feature(prelude_import)] +//! # Elefren: API Wrapper around the Mastodon API. +//! +//! Most of the api is documented on [Mastodon's website](https://docs.joinmastodon.org/client/intro/) +//! +//! ```no_run +//! # extern crate elefren; +//! # fn main() { +//! # run().unwrap(); +//! # } +//! # fn run() -> elefren::Result<()> { +//! use elefren::{helpers::cli, prelude::*}; +//! +//! let registration = Registration::new("https://mastodon.social") +//! .client_name("elefren_test") +//! .build()?; +//! let mastodon = cli::authenticate(registration)?; +//! +//! println!( +//! "{:?}", +//! mastodon +//! .get_home_timeline()? +//! .items_iter() +//! .take(100) +//! .collect::>() +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! Elefren also supports Mastodon's Streaming API: +//! +//! # Example +//! +//! ```no_run +//! # extern crate elefren; +//! # use elefren::prelude::*; +//! # use std::error::Error; +//! use elefren::entities::event::Event; +//! # fn main() -> Result<(), Box> { +//! # let data = Data { +//! # base: "".into(), +//! # client_id: "".into(), +//! # client_secret: "".into(), +//! # redirect: "".into(), +//! # token: "".into(), +//! # }; +//! let client = Mastodon::from(data); +//! for event in client.streaming_user()? { +//! match event { +//! Event::Update(ref status) => { /* .. */ }, +//! Event::Notification(ref notification) => { /* .. */ }, +//! Event::Delete(ref id) => { /* .. */ }, +//! Event::FiltersChanged => { /* .. */ }, +//! } +//! } +//! # Ok(()) +//! # } +//! ``` + +#![deny(missing_docs, warnings, missing_debug_implementations, + missing_copy_implementations, trivial_casts, trivial_numeric_casts, + unsafe_code, unstable_features, unused_import_braces, + unused_qualifications)] +#![allow(intra_doc_link_resolution_failure)] +#[prelude_import] +use std::prelude::v1::*; +#[macro_use] +extern crate std; + +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate doc_comment; +extern crate hyper_old_types; +extern crate isolang; +#[macro_use] +extern crate serde_json; +extern crate chrono; +extern crate reqwest; +extern crate serde; +extern crate serde_qs; +extern crate serde_urlencoded; +extern crate tap_reader; +extern crate try_from; +extern crate url; +extern crate tungstenite; + + + + + +use std::{borrow::Cow, + /* + io::BufRead, + */ + ops}; + +use async_trait::async_trait; +use reqwest::{Client, RequestBuilder, Response}; +use tap_reader::Tap; +/* +use tungstenite::client::AutoStream; +*/ + +use crate::entities::prelude::*; +use crate::page::Page; + +pub use crate::data::Data; +pub use crate::errors::{ApiError, Error, Result}; +pub use isolang::Language; +pub use crate::mastodon_client::{MastodonClient, MastodonUnauthenticated}; +pub use crate::registration::Registration; +pub use crate::requests::{AddFilterRequest, AddPushRequest, StatusesRequest, + UpdateCredsRequest, UpdatePushRequest}; +pub use crate::status_builder::{NewStatus, StatusBuilder}; + +/// Registering your App +pub mod apps { + + + + + + + /* + type Stream = EventReader; + */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /* + /// returns events that are relevant to the authorized user, i.e. home + /// timeline & notifications + /// + /// # Example + /// + /// ```no_run + /// # extern crate elefren; + /// # use elefren::prelude::*; + /// # use std::error::Error; + /// use elefren::entities::event::Event; + /// # fn main() -> Result<(), Box> { + /// # let data = Data { + /// # base: "".into(), + /// # client_id: "".into(), + /// # client_secret: "".into(), + /// # redirect: "".into(), + /// # token: "".into(), + /// # }; + /// let client = Mastodon::from(data); + /// for event in client.streaming_user()? { + /// match event { + /// Event::Update(ref status) => { /* .. */ }, + /// Event::Notification(ref notification) => { /* .. */ }, + /// Event::Delete(ref id) => { /* .. */ }, + /// Event::FiltersChanged => { /* .. */ }, + /// } + /// } + /// # Ok(()) + /// # } + /// ``` + fn streaming_user(&self) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "user"); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + + /// returns all public statuses + fn streaming_public(&self) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "public"); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + + /// Returns all local statuses + fn streaming_local(&self) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "public:local"); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + + /// Returns all public statuses for a particular hashtag + fn streaming_public_hashtag(&self, hashtag: &str) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "hashtag") + .append_pair("tag", hashtag); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + + /// Returns all local statuses for a particular hashtag + fn streaming_local_hashtag(&self, hashtag: &str) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "hashtag:local") + .append_pair("tag", hashtag); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + + /// Returns statuses for a list + fn streaming_list(&self, list_id: &str) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "list") + .append_pair("list", list_id); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + + /// Returns all direct messages + fn streaming_direct(&self) -> Result { + let mut url: url::Url = self.route("/api/v1/streaming").parse()?; + url.query_pairs_mut() + .append_pair("access_token", &self.token) + .append_pair("stream", "direct"); + let mut url: url::Url = reqwest::get(url.as_str())?.url().as_str().parse()?; + let new_scheme = match url.scheme() { + "http" => "ws", + "https" => "wss", + x => return Err(Error::Other(format!("Bad URL scheme: {}", x))), + }; + url.set_scheme(new_scheme).map_err(|_| Error::Other("Bad URL scheme!".to_string()))?; + + let client = tungstenite::connect(url.as_str())?.0; + + Ok(EventReader(WebSocket(client))) + } + */ + + /* + #[derive(Debug)] + /// WebSocket newtype so that EventStream can be implemented without coherency issues + pub struct WebSocket(tungstenite::protocol::WebSocket); + + /// A type that streaming events can be read from + pub trait EventStream { + /// Read a message from this stream + fn read_message(&mut self) -> Result; + } + + impl EventStream for R { + fn read_message(&mut self) -> Result { + let mut buf = String::new(); + self.read_line(&mut buf)?; + Ok(buf) + } + } + + impl EventStream for WebSocket { + fn read_message(&mut self) -> Result { + Ok(self.0.read_message()?.into_text()?) + } + } + + #[derive(Debug)] + /// Iterator that produces events from a mastodon streaming API event stream + pub struct EventReader(R); + impl Iterator for EventReader { + type Item = Event; + + fn next(&mut self) -> Option { + let mut lines = Vec::new(); + loop { + if let Ok(line) = self.0.read_message() { + let line = line.trim().to_string(); + if line.starts_with(":") || line.is_empty() { + continue; + } + lines.push(line); + if let Ok(event) = self.make_event(&lines) { + lines.clear(); + return Some(event); + } else { + continue; + } + } + } + } + } + + impl EventReader { + fn make_event(&self, lines: &[String]) -> Result { + let event; + let data; + if let Some(event_line) = lines + .iter() + .find(|line| line.starts_with("event:")) + { + event = event_line[6..].trim().to_string(); + data = lines.iter().find(|line| line.starts_with("data:")).map(|x| x[5..].trim().to_string()); + } else { + #[derive(Deserialize)] + struct Message { + pub event: String, + pub payload: Option, + } + let message = serde_json::from_str::(&lines[0])?; + event = message.event; + data = message.payload; + } + let event: &str = &event; + Ok(match event { + "notification" => { + let data = data.ok_or_else(|| { + Error::Other("Missing `data` line for notification".to_string()) + })?; + let notification = serde_json::from_str::(&data)?; + Event::Notification(notification) + }, + "update" => { + let data = + data.ok_or_else(|| Error::Other("Missing `data` line for update".to_string()))?; + let status = serde_json::from_str::(&data)?; + Event::Update(status) + }, + "delete" => { + let data = + data.ok_or_else(|| Error::Other("Missing `data` line for delete".to_string()))?; + Event::Delete(data) + }, + "filters_changed" => Event::FiltersChanged, + _ => return Err(Error::Other(format!("Unknown event `{}`", event))), + }) + } + } + */ + + + + + + + + + + + + + + + + // Convert the HTTP response body from JSON. Pass up deserialization errors + // transparently. + + // If deserializing into the desired type fails try again to + // see if this is an error response. + use std::borrow::Cow; + use try_from::TryInto; + use crate::errors::{Error, Result}; + use crate::scopes::Scopes; + /// Represents an application that can be registered with a mastodon instance + pub struct App { + client_name: String, + redirect_uris: String, + scopes: Scopes, + #[serde(skip_serializing_if = "Option::is_none")] + website: Option, + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::clone::Clone for App { + #[inline] + fn clone(&self) -> App { + match *self { + App { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => + App{client_name: ::core::clone::Clone::clone(&(*__self_0_0)), + redirect_uris: + ::core::clone::Clone::clone(&(*__self_0_1)), + scopes: ::core::clone::Clone::clone(&(*__self_0_2)), + website: ::core::clone::Clone::clone(&(*__self_0_3)),}, + } + } + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::fmt::Debug for App { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + App { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => { + let mut debug_trait_builder = f.debug_struct("App"); + let _ = + debug_trait_builder.field("client_name", + &&(*__self_0_0)); + let _ = + debug_trait_builder.field("redirect_uris", + &&(*__self_0_1)); + let _ = + debug_trait_builder.field("scopes", &&(*__self_0_2)); + let _ = + debug_trait_builder.field("website", &&(*__self_0_3)); + debug_trait_builder.finish() + } + } + } + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::default::Default for App { + #[inline] + fn default() -> App { + App{client_name: ::core::default::Default::default(), + redirect_uris: ::core::default::Default::default(), + scopes: ::core::default::Default::default(), + website: ::core::default::Default::default(),} + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _IMPL_SERIALIZE_FOR_App: () = + { + #[allow(rust_2018_idioms, clippy :: useless_attribute)] + extern crate serde as _serde; + #[allow(unused_macros)] + macro_rules! try { + ($ __expr : expr) => + { + match $ __expr + { + _serde :: export :: Ok(__val) => __val, _serde :: + export :: Err(__err) => + { return _serde :: export :: Err(__err) ; } + } + } + } + #[automatically_derived] + impl _serde::Serialize for App { + fn serialize<__S>(&self, __serializer: __S) + -> _serde::export::Result<__S::Ok, __S::Error> where + __S: _serde::Serializer { + let mut __serde_state = + match _serde::Serializer::serialize_struct(__serializer, + "App", + false as + usize + + 1 + 1 + + 1 + + if Option::is_none(&self.website) + { + 0 + } else { + 1 + }) { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "client_name", + &self.client_name) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "redirect_uris", + &self.redirect_uris) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "scopes", + &self.scopes) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + if !Option::is_none(&self.website) { + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "website", + &self.website) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + } else { + match _serde::ser::SerializeStruct::skip_field(&mut __serde_state, + "website") + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + } + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + impl ::core::marker::StructuralPartialEq for App { } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::cmp::PartialEq for App { + #[inline] + fn eq(&self, other: &App) -> bool { + match *other { + App { + client_name: ref __self_1_0, + redirect_uris: ref __self_1_1, + scopes: ref __self_1_2, + website: ref __self_1_3 } => + match *self { + App { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => + (*__self_0_0) == (*__self_1_0) && + (*__self_0_1) == (*__self_1_1) && + (*__self_0_2) == (*__self_1_2) && + (*__self_0_3) == (*__self_1_3), + }, + } + } + #[inline] + fn ne(&self, other: &App) -> bool { + match *other { + App { + client_name: ref __self_1_0, + redirect_uris: ref __self_1_1, + scopes: ref __self_1_2, + website: ref __self_1_3 } => + match *self { + App { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => + (*__self_0_0) != (*__self_1_0) || + (*__self_0_1) != (*__self_1_1) || + (*__self_0_2) != (*__self_1_2) || + (*__self_0_3) != (*__self_1_3), + }, + } + } + } + impl App { + /// Get an AppBuilder object + /// + /// # Example + /// + /// ``` + /// # extern crate elefren; + /// use elefren::apps::App; + /// + /// let mut builder = App::builder(); + /// ``` + pub fn builder<'a>() -> AppBuilder<'a> { AppBuilder::new() } + /// Retrieve the list of scopes that apply to this App + /// + /// # Example + /// + /// ``` + /// # extern crate elefren; + /// # use elefren::Error; + /// use elefren::{apps::App, scopes::Scopes}; + /// + /// # fn main() -> Result<(), Error> { + /// let mut builder = App::builder(); + /// builder.client_name("elefren-test"); + /// let app = builder.build()?; + /// let scopes = app.scopes(); + /// assert_eq!(scopes, &Scopes::read_all()); + /// # Ok(()) + /// # } + /// ``` + pub fn scopes(&self) -> &Scopes { &self.scopes } + } + /// Builder struct for defining your application. + /// ``` + /// use elefren::apps::App; + /// use std::error::Error; + /// + /// # fn main() -> Result<(), Box> { + /// let mut builder = App::builder(); + /// builder.client_name("elefren_test"); + /// let app = builder.build()?; + /// # Ok(()) + /// # } + /// ``` + pub struct AppBuilder<'a> { + client_name: Option>, + redirect_uris: Option>, + scopes: Option, + website: Option>, + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl <'a> ::core::clone::Clone for AppBuilder<'a> { + #[inline] + fn clone(&self) -> AppBuilder<'a> { + match *self { + AppBuilder { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => + AppBuilder{client_name: + ::core::clone::Clone::clone(&(*__self_0_0)), + redirect_uris: + ::core::clone::Clone::clone(&(*__self_0_1)), + scopes: + ::core::clone::Clone::clone(&(*__self_0_2)), + website: + ::core::clone::Clone::clone(&(*__self_0_3)),}, + } + } + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl <'a> ::core::fmt::Debug for AppBuilder<'a> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + AppBuilder { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => { + let mut debug_trait_builder = + f.debug_struct("AppBuilder"); + let _ = + debug_trait_builder.field("client_name", + &&(*__self_0_0)); + let _ = + debug_trait_builder.field("redirect_uris", + &&(*__self_0_1)); + let _ = + debug_trait_builder.field("scopes", &&(*__self_0_2)); + let _ = + debug_trait_builder.field("website", &&(*__self_0_3)); + debug_trait_builder.finish() + } + } + } + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl <'a> ::core::default::Default for AppBuilder<'a> { + #[inline] + fn default() -> AppBuilder<'a> { + AppBuilder{client_name: ::core::default::Default::default(), + redirect_uris: ::core::default::Default::default(), + scopes: ::core::default::Default::default(), + website: ::core::default::Default::default(),} + } + } + impl <'a> ::core::marker::StructuralPartialEq for AppBuilder<'a> { } + #[automatically_derived] + #[allow(unused_qualifications)] + impl <'a> ::core::cmp::PartialEq for AppBuilder<'a> { + #[inline] + fn eq(&self, other: &AppBuilder<'a>) -> bool { + match *other { + AppBuilder { + client_name: ref __self_1_0, + redirect_uris: ref __self_1_1, + scopes: ref __self_1_2, + website: ref __self_1_3 } => + match *self { + AppBuilder { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => + (*__self_0_0) == (*__self_1_0) && + (*__self_0_1) == (*__self_1_1) && + (*__self_0_2) == (*__self_1_2) && + (*__self_0_3) == (*__self_1_3), + }, + } + } + #[inline] + fn ne(&self, other: &AppBuilder<'a>) -> bool { + match *other { + AppBuilder { + client_name: ref __self_1_0, + redirect_uris: ref __self_1_1, + scopes: ref __self_1_2, + website: ref __self_1_3 } => + match *self { + AppBuilder { + client_name: ref __self_0_0, + redirect_uris: ref __self_0_1, + scopes: ref __self_0_2, + website: ref __self_0_3 } => + (*__self_0_0) != (*__self_1_0) || + (*__self_0_1) != (*__self_1_1) || + (*__self_0_2) != (*__self_1_2) || + (*__self_0_3) != (*__self_1_3), + }, + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _IMPL_SERIALIZE_FOR_AppBuilder: () = + { + #[allow(rust_2018_idioms, clippy :: useless_attribute)] + extern crate serde as _serde; + #[allow(unused_macros)] + macro_rules! try { + ($ __expr : expr) => + { + match $ __expr + { + _serde :: export :: Ok(__val) => __val, _serde :: + export :: Err(__err) => + { return _serde :: export :: Err(__err) ; } + } + } + } + #[automatically_derived] + impl <'a> _serde::Serialize for AppBuilder<'a> { + fn serialize<__S>(&self, __serializer: __S) + -> _serde::export::Result<__S::Ok, __S::Error> where + __S: _serde::Serializer { + let mut __serde_state = + match _serde::Serializer::serialize_struct(__serializer, + "AppBuilder", + false as + usize + + 1 + 1 + + 1 + 1) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "client_name", + &self.client_name) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "redirect_uris", + &self.redirect_uris) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "scopes", + &self.scopes) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + match _serde::ser::SerializeStruct::serialize_field(&mut __serde_state, + "website", + &self.website) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + }; + _serde::ser::SerializeStruct::end(__serde_state) + } + } + }; + impl <'a> AppBuilder<'a> { + /// Creates a new AppBuilder object + pub fn new() -> Self { Default::default() } + /// Name of the application. Will be displayed when the user is deciding to + /// grant permission. + /// + /// In order to turn this builder into an App, this needs to be provided + pub fn client_name>>(&mut self, name: I) + -> &mut Self { + self.client_name = Some(name.into()); + self + } + /// Where the user should be redirected after authorization + /// + /// If none is specified, the default is `urn:ietf:wg:oauth:2.0:oob` + pub fn redirect_uris>>(&mut self, uris: I) + -> &mut Self { + self.redirect_uris = Some(uris.into()); + self + } + /// Permission scope of the application. + /// + /// IF none is specified, the default is Scopes::read_all() + pub fn scopes(&mut self, scopes: Scopes) -> &mut Self { + self.scopes = Some(scopes); + self + } + /// URL to the homepage of your application. + pub fn website>>(&mut self, website: I) + -> &mut Self { + self.website = Some(website.into()); + self + } + /// Attempts to convert this build into an `App` + /// + /// Will fail if no `client_name` was provided + pub fn build(self) -> Result { + Ok(App{client_name: + self.client_name.ok_or_else(|| + Error::MissingField("client_name"))?.into(), + redirect_uris: + self.redirect_uris.unwrap_or_else(|| + "urn:ietf:wg:oauth:2.0:oob".into()).into(), + scopes: self.scopes.unwrap_or_else(|| Scopes::read_all()), + website: self.website.map(|s| s.into()),}) + } + } + impl TryInto for App { + type Err = Error; + fn try_into(self) -> Result { Ok(self) } + } + impl <'a> TryInto for AppBuilder<'a> { + type Err = Error; + fn try_into(self) -> Result { Ok(self.build()?) } + } +} +/// Contains the struct that holds the client auth data +pub mod data { + use std::borrow::Cow; + /// Raw data about mastodon app. Save `Data` using `serde` to prevent needing + /// to authenticate on every run. + pub struct Data { + /// Base url of instance eg. `https://mastodon.social`. + pub base: Cow<'static, str>, + /// The client's id given by the instance. + pub client_id: Cow<'static, str>, + /// The client's secret given by the instance. + pub client_secret: Cow<'static, str>, + /// Url to redirect back to your application from the instance signup. + pub redirect: Cow<'static, str>, + /// The client's access token. + pub token: Cow<'static, str>, + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::clone::Clone for Data { + #[inline] + fn clone(&self) -> Data { + match *self { + Data { + base: ref __self_0_0, + client_id: ref __self_0_1, + client_secret: ref __self_0_2, + redirect: ref __self_0_3, + token: ref __self_0_4 } => + Data{base: ::core::clone::Clone::clone(&(*__self_0_0)), + client_id: ::core::clone::Clone::clone(&(*__self_0_1)), + client_secret: + ::core::clone::Clone::clone(&(*__self_0_2)), + redirect: ::core::clone::Clone::clone(&(*__self_0_3)), + token: ::core::clone::Clone::clone(&(*__self_0_4)),}, + } + } + } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::fmt::Debug for Data { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Data { + base: ref __self_0_0, + client_id: ref __self_0_1, + client_secret: ref __self_0_2, + redirect: ref __self_0_3, + token: ref __self_0_4 } => { + let mut debug_trait_builder = f.debug_struct("Data"); + let _ = + debug_trait_builder.field("base", &&(*__self_0_0)); + let _ = + debug_trait_builder.field("client_id", + &&(*__self_0_1)); + let _ = + debug_trait_builder.field("client_secret", + &&(*__self_0_2)); + let _ = + debug_trait_builder.field("redirect", + &&(*__self_0_3)); + let _ = + debug_trait_builder.field("token", &&(*__self_0_4)); + debug_trait_builder.finish() + } + } + } + } + impl ::core::marker::StructuralPartialEq for Data { } + #[automatically_derived] + #[allow(unused_qualifications)] + impl ::core::cmp::PartialEq for Data { + #[inline] + fn eq(&self, other: &Data) -> bool { + match *other { + Data { + base: ref __self_1_0, + client_id: ref __self_1_1, + client_secret: ref __self_1_2, + redirect: ref __self_1_3, + token: ref __self_1_4 } => + match *self { + Data { + base: ref __self_0_0, + client_id: ref __self_0_1, + client_secret: ref __self_0_2, + redirect: ref __self_0_3, + token: ref __self_0_4 } => + (*__self_0_0) == (*__self_1_0) && + (*__self_0_1) == (*__self_1_1) && + (*__self_0_2) == (*__self_1_2) && + (*__self_0_3) == (*__self_1_3) && + (*__self_0_4) == (*__self_1_4), + }, + } + } + #[inline] + fn ne(&self, other: &Data) -> bool { + match *other { + Data { + base: ref __self_1_0, + client_id: ref __self_1_1, + client_secret: ref __self_1_2, + redirect: ref __self_1_3, + token: ref __self_1_4 } => + match *self { + Data { + base: ref __self_0_0, + client_id: ref __self_0_1, + client_secret: ref __self_0_2, + redirect: ref __self_0_3, + token: ref __self_0_4 } => + (*__self_0_0) != (*__self_1_0) || + (*__self_0_1) != (*__self_1_1) || + (*__self_0_2) != (*__self_1_2) || + (*__self_0_3) != (*__self_1_3) || + (*__self_0_4) != (*__self_1_4), + }, + } + } + } + #[doc(hidden)] + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _IMPL_DESERIALIZE_FOR_Data: () = + { + #[allow(rust_2018_idioms, clippy :: useless_attribute)] + extern crate serde as _serde; + #[allow(unused_macros)] + macro_rules! try { + ($ __expr : expr) => + { + match $ __expr + { + _serde :: export :: Ok(__val) => __val, _serde :: + export :: Err(__err) => + { return _serde :: export :: Err(__err) ; } + } + } + } + #[automatically_derived] + impl <'de> _serde::Deserialize<'de> for Data { + fn deserialize<__D>(__deserializer: __D) + -> _serde::export::Result where + __D: _serde::Deserializer<'de> { + #[allow(non_camel_case_types)] + enum __Field { + __field0, + __field1, + __field2, + __field3, + __field4, + __ignore, + } + struct __FieldVisitor; + impl <'de> _serde::de::Visitor<'de> for __FieldVisitor { + type Value = __Field; + fn expecting(&self, + __formatter: + &mut _serde::export::Formatter) + -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(__formatter, + "field identifier") + } + fn visit_u64<__E>(self, __value: u64) + -> _serde::export::Result where + __E: _serde::de::Error { + match __value { + 0u64 => _serde::export::Ok(__Field::__field0), + 1u64 => _serde::export::Ok(__Field::__field1), + 2u64 => _serde::export::Ok(__Field::__field2), + 3u64 => _serde::export::Ok(__Field::__field3), + 4u64 => _serde::export::Ok(__Field::__field4), + _ => + _serde::export::Err(_serde::de::Error::invalid_value(_serde::de::Unexpected::Unsigned(__value), + &"field index 0 <= i < 5")), + } + } + fn visit_str<__E>(self, __value: &str) + -> _serde::export::Result where + __E: _serde::de::Error { + match __value { + "base" => + _serde::export::Ok(__Field::__field0), + "client_id" => + _serde::export::Ok(__Field::__field1), + "client_secret" => + _serde::export::Ok(__Field::__field2), + "redirect" => + _serde::export::Ok(__Field::__field3), + "token" => + _serde::export::Ok(__Field::__field4), + _ => { _serde::export::Ok(__Field::__ignore) } + } + } + fn visit_bytes<__E>(self, __value: &[u8]) + -> _serde::export::Result where + __E: _serde::de::Error { + match __value { + b"base" => + _serde::export::Ok(__Field::__field0), + b"client_id" => + _serde::export::Ok(__Field::__field1), + b"client_secret" => + _serde::export::Ok(__Field::__field2), + b"redirect" => + _serde::export::Ok(__Field::__field3), + b"token" => + _serde::export::Ok(__Field::__field4), + _ => { _serde::export::Ok(__Field::__ignore) } + } + } + } + impl <'de> _serde::Deserialize<'de> for __Field { + #[inline] + fn deserialize<__D>(__deserializer: __D) + -> _serde::export::Result where + __D: _serde::Deserializer<'de> { + _serde::Deserializer::deserialize_identifier(__deserializer, + __FieldVisitor) + } + } + struct __Visitor<'de> { + marker: _serde::export::PhantomData, + lifetime: _serde::export::PhantomData<&'de ()>, + } + impl <'de> _serde::de::Visitor<'de> for __Visitor<'de> { + type Value = Data; + fn expecting(&self, + __formatter: + &mut _serde::export::Formatter) + -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(__formatter, + "struct Data") + } + #[inline] + fn visit_seq<__A>(self, mut __seq: __A) + -> _serde::export::Result + where __A: _serde::de::SeqAccess<'de> { + let __field0 = + match match _serde::de::SeqAccess::next_element::>(&mut __seq) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + } { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length(0usize, + &"struct Data with 5 elements")); + } + }; + let __field1 = + match match _serde::de::SeqAccess::next_element::>(&mut __seq) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + } { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length(1usize, + &"struct Data with 5 elements")); + } + }; + let __field2 = + match match _serde::de::SeqAccess::next_element::>(&mut __seq) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + } { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length(2usize, + &"struct Data with 5 elements")); + } + }; + let __field3 = + match match _serde::de::SeqAccess::next_element::>(&mut __seq) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + } { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length(3usize, + &"struct Data with 5 elements")); + } + }; + let __field4 = + match match _serde::de::SeqAccess::next_element::>(&mut __seq) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + } { + _serde::export::Some(__value) => __value, + _serde::export::None => { + return _serde::export::Err(_serde::de::Error::invalid_length(4usize, + &"struct Data with 5 elements")); + } + }; + _serde::export::Ok(Data{base: __field0, + client_id: __field1, + client_secret: __field2, + redirect: __field3, + token: __field4,}) + } + #[inline] + fn visit_map<__A>(self, mut __map: __A) + -> _serde::export::Result + where __A: _serde::de::MapAccess<'de> { + let mut __field0: + _serde::export::Option> = + _serde::export::None; + let mut __field1: + _serde::export::Option> = + _serde::export::None; + let mut __field2: + _serde::export::Option> = + _serde::export::None; + let mut __field3: + _serde::export::Option> = + _serde::export::None; + let mut __field4: + _serde::export::Option> = + _serde::export::None; + while let _serde::export::Some(__key) = + match _serde::de::MapAccess::next_key::<__Field>(&mut __map) + { + _serde::export::Ok(__val) => __val, + _serde::export::Err(__err) => { + return _serde::export::Err(__err); + } + } { + match __key { + __Field::__field0 => { + if _serde::export::Option::is_some(&__field0) + { + return _serde::export::Err(<__A::Error + as + _serde::de::Error>::duplicate_field("base")); + } + __field0 = + _serde::export::Some(match _serde::de::MapAccess::next_value::>(&mut __map) + { + _serde::export::Ok(__val) + => __val, + _serde::export::Err(__err) + => { + return _serde::export::Err(__err); + } + }); + } + __Field::__field1 => { + if _serde::export::Option::is_some(&__field1) + { + return _serde::export::Err(<__A::Error + as + _serde::de::Error>::duplicate_field("client_id")); + } + __field1 = + _serde::export::Some(match _serde::de::MapAccess::next_value::>(&mut __map) + { +