feat(update_credentials): Implement UpdateCredsRequest

This will now allow a user to actually call the
MastodonClient::update_credentials method
master
Paul Woolcock 6 years ago
parent 6befd79935
commit a57c7e2f91
  1. 149
      src/entities/account.rs
  2. 2
      src/entities/mod.rs
  3. 5
      src/lib.rs
  4. 4
      src/mastodon_client.rs
  5. 9
      src/requests/mod.rs
  6. 276
      src/requests/update_credentials.rs

@ -1,10 +1,9 @@
//! A module containing everything relating to a account returned from the api. //! A module containing everything relating to a account returned from the api.
use chrono::prelude::*; use chrono::prelude::*;
use reqwest::multipart::Form;
use serde::de::{self, Deserialize, Deserializer, Unexpected}; use serde::de::{self, Deserialize, Deserializer, Unexpected};
use std::path::Path; use status_builder;
use Result; use std::path::PathBuf;
/// A struct representing an Account. /// A struct representing an Account.
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@ -51,7 +50,7 @@ pub struct Account {
/// An extra object given from `verify_credentials` giving defaults about a user /// An extra object given from `verify_credentials` giving defaults about a user
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct Source { pub struct Source {
privacy: ::status_builder::Visibility, privacy: status_builder::Visibility,
#[serde(deserialize_with = "string_or_bool")] #[serde(deserialize_with = "string_or_bool")]
sensitive: bool, sensitive: bool,
note: String, note: String,
@ -82,130 +81,24 @@ fn string_or_bool<'de, D: Deserializer<'de>>(val: D) -> ::std::result::Result<bo
}) })
} }
/// Data structure used for updating user credentials #[derive(Debug, Default, Clone, Copy, Serialize)]
#[derive(Debug)] pub(crate) struct UpdateSource {
pub struct CredentialsBuilder<'a> { #[serde(skip_serializing_if = "Option::is_none")]
display_name: Option<&'a str>, pub(crate) privacy: Option<status_builder::Visibility>,
note: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")]
avatar: Option<&'a Path>, pub(crate) sensitive: Option<bool>,
header: Option<&'a Path>,
} }
impl<'a> CredentialsBuilder<'a> { #[derive(Debug, Default, Serialize)]
/// Turns a `CredentialsForm` into a form suitable for PUTing to the pub(crate) struct Credentials {
/// endpoint #[serde(skip_serializing_if = "Option::is_none")]
pub fn into_form(self) -> Result<Form> { pub(crate) display_name: Option<String>,
let mut form = Form::new(); #[serde(skip_serializing_if = "Option::is_none")]
macro_rules! add_to_form { pub(crate) note: Option<String>,
($key:ident : Text; $($rest:tt)*) => {{ #[serde(skip_serializing_if = "Option::is_none")]
if let Some(val) = self.$key { pub(crate) avatar: Option<PathBuf>,
form = form.text(stringify!($key), val.to_owned()); #[serde(skip_serializing_if = "Option::is_none")]
} pub(crate) header: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
add_to_form!{$($rest)*} pub(crate) source: Option<UpdateSource>,
}};
($key:ident : File; $($rest:tt)*) => {{
if let Some(val) = self.$key {
form = form.file(stringify!($key), val)?;
}
add_to_form!{$($rest)*}
}};
() => {}
}
add_to_form! {
display_name: Text;
note: Text;
avatar: File;
header: File;
}
Ok(form)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
#[test]
fn test_credentials_builder_to_form() {
let avatar = NamedTempFile::new().expect("Couldn't make avatar file");
let header = NamedTempFile::new().expect("Couldn't make header file");
let tests = [
(None, None, None, None),
(Some("my-display-name"), None, None, None),
(None, Some("my-note"), None, None),
(None, None, Some(avatar.path().clone()), None),
(None, None, None, Some(header.path().clone())),
(Some("my-display-name"), Some("my-note"), None, None),
(
Some("my-display-name"),
None,
Some(avatar.path().clone()),
None,
),
(None, Some("my-note"), Some(avatar.path().clone()), None),
(None, Some("my-note"), None, Some(header.path().clone())),
(
None,
None,
Some(avatar.path().clone()),
Some(header.path().clone()),
),
(
Some("my-display-name"),
None,
None,
Some(header.path().clone()),
),
(
Some("my-display-name"),
Some("my-note"),
Some(avatar.path().clone()),
None,
),
(
Some("my-display-name"),
Some("my-note"),
None,
Some(header.path().clone()),
),
(
Some("my-display-name"),
None,
Some(avatar.path().clone()),
Some(header.path().clone()),
),
(
None,
Some("my-note"),
Some(avatar.path().clone()),
Some(header.path().clone()),
),
(
Some("my-display-name"),
Some("my-note"),
Some(avatar.path().clone()),
Some(header.path().clone()),
),
];
for test in tests.into_iter() {
let (display_name, note, avatar, header) = test;
let credentials_builder = CredentialsBuilder {
display_name: *display_name,
note: *note,
avatar: *avatar,
header: *header,
};
let _form = credentials_builder
.into_form()
.expect("could not create form");
}
}
} }

@ -33,7 +33,7 @@ pub struct Empty {}
/// modules: /// modules:
pub mod prelude { pub mod prelude {
pub use super::{ pub use super::{
account::{Account, CredentialsBuilder, Source}, account::{Account, Source},
attachment::{Attachment, MediaType}, attachment::{Attachment, MediaType},
card::Card, card::Card,
context::Context, context::Context,

@ -80,7 +80,7 @@ pub use data::Data;
pub use errors::{ApiError, Error, Result}; pub use errors::{ApiError, Error, Result};
pub use mastodon_client::MastodonClient; pub use mastodon_client::MastodonClient;
pub use registration::Registration; pub use registration::Registration;
pub use requests::statuses::StatusesRequest; pub use requests::{StatusesRequest, UpdateCredsRequest};
pub use status_builder::StatusBuilder; pub use status_builder::StatusBuilder;
/// Registering your App /// Registering your App
@ -207,7 +207,8 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
(delete) delete_status: "statuses/{}" => Empty, (delete) delete_status: "statuses/{}" => Empty,
} }
fn update_credentials(&self, changes: CredentialsBuilder) -> Result<Account> { fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> {
let changes = builder.build()?;
let url = self.route("/api/v1/accounts/update_credentials"); let url = self.route("/api/v1/accounts/update_credentials");
let response = self.send(self.client.patch(&url).json(&changes))?; let response = self.send(self.client.patch(&url).json(&changes))?;

@ -4,7 +4,7 @@ use entities::prelude::*;
use errors::Result; use errors::Result;
use http_send::{HttpSend, HttpSender}; use http_send::{HttpSend, HttpSender};
use page::Page; use page::Page;
use requests::statuses::StatusesRequest; use requests::{StatusesRequest, UpdateCredsRequest};
use status_builder::StatusBuilder; use status_builder::StatusBuilder;
/// Represents the set of methods that a Mastodon Client can do, so that /// Represents the set of methods that a Mastodon Client can do, so that
@ -172,7 +172,7 @@ pub trait MastodonClient<H: HttpSend = HttpSender> {
unimplemented!("This method was not implemented"); unimplemented!("This method was not implemented");
} }
/// PATCH /api/v1/accounts/update_credentials /// PATCH /api/v1/accounts/update_credentials
fn update_credentials(&self, changes: CredentialsBuilder) -> Result<Account> { fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result<Account> {
unimplemented!("This method was not implemented"); unimplemented!("This method was not implemented");
} }
/// POST /api/v1/statuses /// POST /api/v1/statuses

@ -1,2 +1,7 @@
/// Data structures for the MastodonClient::statuses method /// Data structure for the MastodonClient::statuses method
pub mod statuses; pub use self::statuses::StatusesRequest;
/// Data structure for the MastodonClient::update_credentials method
pub use self::update_credentials::UpdateCredsRequest;
mod statuses;
mod update_credentials;

@ -0,0 +1,276 @@
use std::{
fmt::Display,
path::{Path, PathBuf},
};
use entities::account::{Credentials, UpdateSource};
use errors::Result;
use status_builder;
/// Builder to pass to the Mastodon::update_credentials method
///
/// # Example
///
/// ```no_run
/// # extern crate elefren;
/// # use elefren::Data;
/// # fn main() -> Result<(), elefren::Error> {
/// # let data = Data {
/// # base: "".into(),
/// # client_id: "".into(),
/// # client_secret: "".into(),
/// # redirect: "".into(),
/// # token: "".into(),
/// # };
/// use elefren::{prelude::*, status_builder::Visibility, UpdateCredsRequest};
///
/// let client = Mastodon::from(data);
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.privacy(Visibility::Unlisted);
///
/// let result = client.update_credentials(&mut builder)?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Default, Clone, PartialEq)]
pub struct UpdateCredsRequest {
display_name: Option<String>,
note: Option<String>,
avatar: Option<PathBuf>,
header: Option<PathBuf>,
// UpdateSource fields
privacy: Option<status_builder::Visibility>,
sensitive: Option<bool>,
}
impl UpdateCredsRequest {
/// Create a new UpdateCredsRequest
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::UpdateCredsRequest;
///
/// let mut builder = UpdateCredsRequest::new();
/// ```
pub fn new() -> UpdateCredsRequest {
Default::default()
}
/// Set the new display_name value
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::UpdateCredsRequest;
///
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.display_name("my new display name");
/// ```
pub fn display_name<D: Display>(&mut self, name: D) -> &mut Self {
self.display_name = Some(name.to_string());
self
}
/// Set the new note value
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::UpdateCredsRequest;
///
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.note("my new note");
/// ```
pub fn note<D: Display>(&mut self, note: D) -> &mut Self {
self.note = Some(note.to_string());
self
}
/// Set the new avatar value
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::UpdateCredsRequest;
///
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.avatar("/path/to/my/new/avatar");
/// ```
pub fn avatar<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
let path = path.as_ref();
let path = path.to_path_buf();
self.avatar = Some(path);
self
}
/// Set the new header value
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::UpdateCredsRequest;
///
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.header("/path/to/my/new/header");
/// ```
pub fn header<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
let path = path.as_ref();
let path = path.to_path_buf();
self.header = Some(path);
self
}
/// Set the new privacy value
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::{status_builder::Visibility, UpdateCredsRequest};
///
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.privacy(Visibility::Public);
/// ```
pub fn privacy(&mut self, privacy: status_builder::Visibility) -> &mut Self {
self.privacy = Some(privacy);
self
}
/// Set the new sensitive value
///
/// # Example
///
/// ```
/// # extern crate elefren;
/// use elefren::UpdateCredsRequest;
///
/// let mut builder = UpdateCredsRequest::new();
///
/// builder.sensitive(true);
/// ```
pub fn sensitive(&mut self, sensitive: bool) -> &mut Self {
self.sensitive = Some(sensitive);
self
}
pub(crate) fn build(&mut self) -> Result<Credentials> {
Ok(Credentials {
display_name: self.display_name.clone(),
note: self.note.clone(),
avatar: self.avatar.clone(),
header: self.avatar.clone(),
source: Some(UpdateSource {
privacy: self.privacy.clone(),
sensitive: self.sensitive.clone(),
}),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use status_builder::Visibility;
#[test]
fn test_update_creds_request_new() {
let builder = UpdateCredsRequest::new();
assert_eq!(
builder,
UpdateCredsRequest {
..Default::default()
}
);
}
#[test]
fn test_update_creds_request_display_name() {
let mut builder = UpdateCredsRequest::new();
builder.display_name("foo");
assert_eq!(
builder,
UpdateCredsRequest {
display_name: Some("foo".into()),
..Default::default()
}
);
}
#[test]
fn test_update_creds_request_note() {
let mut builder = UpdateCredsRequest::new();
builder.note("foo");
assert_eq!(
builder,
UpdateCredsRequest {
note: Some("foo".into()),
..Default::default()
}
);
}
#[test]
fn test_update_creds_request_avatar() {
let mut builder = UpdateCredsRequest::new();
builder.avatar("/path/to/avatar.png");
assert_eq!(
builder,
UpdateCredsRequest {
avatar: Some(Path::new("/path/to/avatar.png").to_path_buf()),
..Default::default()
}
);
}
#[test]
fn test_update_creds_request_header() {
let mut builder = UpdateCredsRequest::new();
builder.header("/path/to/header.png");
assert_eq!(
builder,
UpdateCredsRequest {
header: Some(Path::new("/path/to/header.png").to_path_buf()),
..Default::default()
}
);
}
#[test]
fn test_update_creds_request_privacy() {
let mut builder = UpdateCredsRequest::new();
builder.privacy(Visibility::Public);
assert_eq!(
builder,
UpdateCredsRequest {
privacy: Some(Visibility::Public),
..Default::default()
}
);
}
#[test]
fn test_update_creds_request_sensitive() {
let mut builder = UpdateCredsRequest::new();
builder.sensitive(true);
assert_eq!(
builder,
UpdateCredsRequest {
sensitive: Some(true),
..Default::default()
}
);
}
}
Loading…
Cancel
Save