duplicate the AppBuilder api in Registration

Closes #13
master
Paul Woolcock 6 years ago
parent ae4d5dffe5
commit 1436c28e42
  1. 6
      README.md
  2. 25
      examples/register.rs
  3. 22
      src/apps.rs
  4. 3
      src/http_send.rs
  5. 7
      src/lib.rs
  6. 114
      src/registration.rs

@ -56,11 +56,9 @@ fn main() -> Result<(), Box<Error>> {
} }
fn register() -> Result<Mastodon, Box<Error>> { fn register() -> Result<Mastodon, Box<Error>> {
let mut app = App::builder();
app.client_name("elefren-examples");
let registration = Registration::new("https://mastodon.social") let registration = Registration::new("https://mastodon.social")
.register(app)?; .client_name("elefren-examples")
.register()?;
let url = registration.authorize_url()?; let url = registration.authorize_url()?;
println!("Click this link to authorize on Mastodon: {}", url); println!("Click this link to authorize on Mastodon: {}", url);

@ -2,16 +2,11 @@
#![cfg_attr(not(feature = "toml"), allow(unused_imports))] #![cfg_attr(not(feature = "toml"), allow(unused_imports))]
extern crate elefren; extern crate elefren;
pub use self::elefren::{Data, MastodonClient}; pub use self::elefren::prelude::*;
pub use self::elefren::apps::prelude::*;
use std::{error::Error, io}; use std::{error::Error, io};
use self::elefren::{
apps::{App, Scopes},
Mastodon,
Registration,
};
#[cfg(feature = "toml")] #[cfg(feature = "toml")]
use self::elefren::data::toml; use self::elefren::data::toml;
@ -34,20 +29,18 @@ pub fn get_mastodon_data() -> Result<Mastodon, Box<Error>> {
#[cfg(feature = "toml")] #[cfg(feature = "toml")]
pub fn register() -> Result<Mastodon, Box<Error>> { pub fn register() -> Result<Mastodon, Box<Error>> {
let mut app = App::builder();
app.client_name("elefren-examples")
.scopes(Scopes::All)
.website("https://github.com/pwoolcoc/elefren");
let website = read_line("Please enter your mastodon instance url:")?; let website = read_line("Please enter your mastodon instance url:")?;
let registration = Registration::new(website.trim()); let registration = Registration::new(website.trim())
let registered = registration.register(app)?; .client_name("elefren-examples")
let url = registered.authorize_url()?; .scopes(Scopes::All)
.website("https://github.com/pwoolcoc/elefren")
.register()?;
let url = registration.authorize_url()?;
println!("Click this link to authorize on Mastodon: {}", url); println!("Click this link to authorize on Mastodon: {}", url);
let code = read_line("Paste the returned authorization code: ")?; let code = read_line("Paste the returned authorization code: ")?;
let mastodon = registered.complete(code)?; let mastodon = registration.complete(code)?;
// 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, "mastodon-data.toml")?;

@ -1,7 +1,5 @@
use std::{borrow::Cow, fmt}; use std::{borrow::Cow, fmt};
use try_from::TryInto;
use errors::{Error, Result}; use errors::{Error, Result};
/// Provides the necessary types for registering an App and getting the /// Provides the necessary types for registering an App and getting the
@ -43,7 +41,7 @@ impl App {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[derive(Debug, Default, Serialize)] #[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct AppBuilder<'a> { pub struct AppBuilder<'a> {
client_name: Option<Cow<'a, str>>, client_name: Option<Cow<'a, str>>,
redirect_uris: Option<Cow<'a, str>>, redirect_uris: Option<Cow<'a, str>>,
@ -106,26 +104,10 @@ impl<'a> AppBuilder<'a> {
} }
} }
impl TryInto<App> for App {
type Err = Error;
fn try_into(self) -> Result<App> {
Ok(self)
}
}
impl<'a> TryInto<App> for AppBuilder<'a> {
type Err = Error;
fn try_into(self) -> Result<App> {
Ok(self.build()?)
}
}
/// Permission scope of the application. /// Permission scope of the application.
/// [Details on what each permission provides][1] /// [Details on what each permission provides][1]
/// [1]: https://github.com/tootsuite/documentation/blob/master/Using-the-API/OAuth-details.md) /// [1]: https://github.com/tootsuite/documentation/blob/master/Using-the-API/OAuth-details.md)
#[derive(Debug, Clone, Copy, Serialize)] #[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub enum Scopes { pub enum Scopes {
/// All Permissions, equivalent to `read write follow` /// All Permissions, equivalent to `read write follow`
#[serde(rename = "read write follow")] #[serde(rename = "read write follow")]

@ -1,7 +1,7 @@
use reqwest::{Client, Request, RequestBuilder, Response}; use reqwest::{Client, Request, RequestBuilder, Response};
use Result; use Result;
pub trait HttpSend { pub trait HttpSend: Clone {
fn execute(&self, client: &Client, request: Request) -> Result<Response>; fn execute(&self, client: &Client, request: Request) -> Result<Response>;
fn send(&self, client: &Client, builder: &mut RequestBuilder) -> Result<Response> { fn send(&self, client: &Client, builder: &mut RequestBuilder) -> Result<Response> {
let request = builder.build()?; let request = builder.build()?;
@ -9,6 +9,7 @@ pub trait HttpSend {
} }
} }
#[derive(Clone)]
pub struct HttpSender; pub struct HttpSender;
impl HttpSend for HttpSender { impl HttpSend for HttpSender {

@ -11,10 +11,9 @@
//! # fn try() -> elefren::Result<()> { //! # fn try() -> elefren::Result<()> {
//! use elefren::{apps::prelude::*, prelude::*}; //! use elefren::{apps::prelude::*, prelude::*};
//! //!
//! let mut app = App::builder(); //! let registration = Registration::new("https://mastodon.social")
//! app.client_name("elefren_test"); //! .client_name("elefren_test")
//! //! .register()?;
//! let registration = Registration::new("https://mastodon.social").register(app)?;
//! let url = registration.authorize_url()?; //! let url = registration.authorize_url()?;
//! // Here you now need to open the url in the browser //! // Here you now need to open the url in the browser
//! // And handle a the redirect url coming back with the code. //! // And handle a the redirect url coming back with the code.

@ -1,19 +1,17 @@
use std::borrow::Cow;
use reqwest::{Client, RequestBuilder, Response}; use reqwest::{Client, RequestBuilder, Response};
use try_from::TryInto;
use apps::{App, Scopes}; use apps::{App, AppBuilder, Scopes};
use http_send::{HttpSend, HttpSender}; use http_send::{HttpSend, HttpSender};
use Data; use {Data, Mastodon, MastodonBuilder, Result};
use Error;
use Mastodon;
use MastodonBuilder;
use Result;
/// Handles registering your mastodon app to your instance. It is recommended /// Handles registering your mastodon app to your instance. It is recommended
/// you cache your data struct to avoid registering on every run. /// you cache your data struct to avoid registering on every run.
pub struct Registration<H: HttpSend> { pub struct Registration<'a, H: HttpSend> {
base: String, base: String,
client: Client, client: Client,
app_builder: AppBuilder<'a>,
http_sender: H, http_sender: H,
} }
@ -34,7 +32,7 @@ struct AccessToken {
access_token: String, access_token: String,
} }
impl Registration<HttpSender> { impl<'a> Registration<'a, HttpSender> {
/// Construct a new registration process to the instance of the `base` url. /// Construct a new registration process to the instance of the `base` url.
/// ``` /// ```
/// use elefren::apps::prelude::*; /// use elefren::apps::prelude::*;
@ -45,21 +43,43 @@ impl Registration<HttpSender> {
Registration { Registration {
base: base.into(), base: base.into(),
client: Client::new(), client: Client::new(),
app_builder: AppBuilder::new(),
http_sender: HttpSender, http_sender: HttpSender,
} }
} }
} }
impl<H: HttpSend> Registration<H> { impl<'a, H: HttpSend> Registration<'a, H> {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn with_sender<I: Into<String>>(base: I, http_sender: H) -> Self { pub(crate) fn with_sender<I: Into<String>>(base: I, http_sender: H) -> Self {
Registration { Registration {
base: base.into(), base: base.into(),
client: Client::new(), client: Client::new(),
app_builder: AppBuilder::new(),
http_sender, http_sender,
} }
} }
pub fn client_name<I: Into<Cow<'a, str>>>(&mut self, name: I) -> &mut Self {
self.app_builder.client_name(name.into());
self
}
pub fn redirect_uris<I: Into<Cow<'a, str>>>(&mut self, uris: I) -> &mut Self {
self.app_builder.redirect_uris(uris);
self
}
pub fn scopes(&mut self, scopes: Scopes) -> &mut Self {
self.app_builder.scopes(scopes);
self
}
pub fn website<I: Into<Cow<'a, str>>>(&mut self, website: I) -> &mut Self {
self.app_builder.website(website);
self
}
fn send(&self, req: &mut RequestBuilder) -> Result<Response> { fn send(&self, req: &mut RequestBuilder) -> Result<Response> {
Ok(self.http_sender.send(&self.client, req)?) Ok(self.http_sender.send(&self.client, req)?)
} }
@ -71,38 +91,32 @@ impl<H: HttpSend> Registration<H> {
/// # fn main () -> elefren::Result<()> { /// # fn main () -> elefren::Result<()> {
/// use elefren::{apps::prelude::*, prelude::*}; /// use elefren::{apps::prelude::*, prelude::*};
/// ///
/// let mut builder = App::builder(); /// let registration = Registration::new("https://mastodon.social")
/// builder.client_name("elefren_test"); /// .client_name("elefren_test")
/// let app = builder.build()?; /// .register()?;
/// /// let url = registration.authorize_url()?;
/// let registration = Registration::new("https://mastodon.social");
/// let registered = registration.register(app)?;
/// let url = registered.authorize_url()?;
/// // Here you now need to open the url in the browser /// // Here you now need to open the url in the browser
/// // And handle a the redirect url coming back with the code. /// // And handle a the redirect url coming back with the code.
/// let code = String::from("RETURNED_FROM_BROWSER"); /// let code = String::from("RETURNED_FROM_BROWSER");
/// let mastodon = registered.complete(code)?; /// let mastodon = registration.complete(code)?;
/// ///
/// println!("{:?}", mastodon.get_home_timeline()?.initial_items); /// println!("{:?}", mastodon.get_home_timeline()?.initial_items);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn register<I: TryInto<App>>(self, app: I) -> Result<Registered<H>> pub fn register(&mut self) -> Result<Registered<H>> {
where let app: App = self.app_builder.clone().build()?;
Error: From<<I as TryInto<App>>::Err>,
{
let app = app.try_into()?;
let url = format!("{}/api/v1/apps", self.base); let url = format!("{}/api/v1/apps", self.base);
let oauth: OAuth = self.send(self.client.post(&url).form(&app))?.json()?; let oauth: OAuth = self.send(self.client.post(&url).form(&app))?.json()?;
Ok(Registered { Ok(Registered {
base: self.base, base: self.base.clone(),
client: self.client, client: self.client.clone(),
client_id: oauth.client_id, client_id: oauth.client_id,
client_secret: oauth.client_secret, client_secret: oauth.client_secret,
redirect: oauth.redirect_uri, redirect: oauth.redirect_uri,
scopes: app.scopes(), scopes: app.scopes(),
http_sender: self.http_sender, http_sender: self.http_sender.clone(),
}) })
} }
} }
@ -160,3 +174,51 @@ pub struct Registered<H: HttpSend> {
scopes: Scopes, scopes: Scopes,
http_sender: H, http_sender: H,
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registration_new() {
let r = Registration::new("https://example.com");
assert_eq!(r.base, "https://example.com".to_string());
assert_eq!(r.app_builder, AppBuilder::new());
}
#[test]
fn test_set_client_name() {
let mut r = Registration::new("https://example.com");
r.client_name("foo-test");
assert_eq!(r.base, "https://example.com".to_string());
assert_eq!(&mut r.app_builder, AppBuilder::new().client_name("foo-test"));
}
#[test]
fn test_set_redirect_uris() {
let mut r = Registration::new("https://example.com");
r.redirect_uris("https://foo.com");
assert_eq!(r.base, "https://example.com".to_string());
assert_eq!(&mut r.app_builder, AppBuilder::new().redirect_uris("https://foo.com"));
}
#[test]
fn test_set_scopes() {
let mut r = Registration::new("https://example.com");
r.scopes(Scopes::All);
assert_eq!(r.base, "https://example.com".to_string());
assert_eq!(&mut r.app_builder, AppBuilder::new().scopes(Scopes::All));
}
#[test]
fn test_set_website() {
let mut r = Registration::new("https://example.com");
r.website("https://website.example.com");
assert_eq!(r.base, "https://example.com".to_string());
assert_eq!(&mut r.app_builder, AppBuilder::new().website("https://website.example.com"));
}
}

Loading…
Cancel
Save