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.
use chrono::prelude::*;
use reqwest::multipart::Form;
use serde::de::{self, Deserialize, Deserializer, Unexpected};
use std::path::Path;
use Result;
use status_builder;
use std::path::PathBuf;
/// A struct representing an Account.
#[derive(Debug, Clone, Deserialize)]
@ -51,7 +50,7 @@ pub struct Account {
/// An extra object given from `verify_credentials` giving defaults about a user
#[derive(Debug, Clone, Deserialize)]
pub struct Source {
privacy: ::status_builder::Visibility,
privacy: status_builder::Visibility,
#[serde(deserialize_with = "string_or_bool")]
sensitive: bool,
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)]
pub struct CredentialsBuilder<'a> {
display_name: Option<&'a str>,
note: Option<&'a str>,
avatar: Option<&'a Path>,
header: Option<&'a Path>,
#[derive(Debug, Default, Clone, Copy, Serialize)]
pub(crate) struct UpdateSource {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) privacy: Option<status_builder::Visibility>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) sensitive: Option<bool>,
}
impl<'a> CredentialsBuilder<'a> {
/// Turns a `CredentialsForm` into a form suitable for PUTing to the
/// endpoint
pub fn into_form(self) -> Result<Form> {
let mut form = Form::new();
macro_rules! add_to_form {
($key:ident : Text; $($rest:tt)*) => {{
if let Some(val) = self.$key {
form = form.text(stringify!($key), val.to_owned());
}
add_to_form!{$($rest)*}
}};
($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");
}
}
#[derive(Debug, Default, Serialize)]
pub(crate) struct Credentials {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) display_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) note: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) avatar: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) header: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) source: Option<UpdateSource>,
}

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

@ -80,7 +80,7 @@ pub use data::Data;
pub use errors::{ApiError, Error, Result};
pub use mastodon_client::MastodonClient;
pub use registration::Registration;
pub use requests::statuses::StatusesRequest;
pub use requests::{StatusesRequest, UpdateCredsRequest};
pub use status_builder::StatusBuilder;
/// Registering your App
@ -207,7 +207,8 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
(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 response = self.send(self.client.patch(&url).json(&changes))?;

@ -4,7 +4,7 @@ use entities::prelude::*;
use errors::Result;
use http_send::{HttpSend, HttpSender};
use page::Page;
use requests::statuses::StatusesRequest;
use requests::{StatusesRequest, UpdateCredsRequest};
use status_builder::StatusBuilder;
/// 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");
}
/// 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");
}
/// POST /api/v1/statuses

@ -1,2 +1,7 @@
/// Data structures for the MastodonClient::statuses method
pub mod statuses;
/// Data structure for the MastodonClient::statuses method
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