diff --git a/src/entities/account.rs b/src/entities/account.rs index e787df5..1a8061a 100644 --- a/src/entities/account.rs +++ b/src/entities/account.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 { - 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, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) sensitive: Option, } -impl<'a> CredentialsBuilder<'a> { - /// Turns a `CredentialsForm` into a form suitable for PUTing to the - /// endpoint - pub fn into_form(self) -> Result
{ - 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, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) note: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) avatar: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) header: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) source: Option, } diff --git a/src/entities/mod.rs b/src/entities/mod.rs index 9d73d8b..fc47b3e 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index bdc962b..da2f109 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 MastodonClient for Mastodon { (delete) delete_status: "statuses/{}" => Empty, } - fn update_credentials(&self, changes: CredentialsBuilder) -> Result { + fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result { + let changes = builder.build()?; let url = self.route("/api/v1/accounts/update_credentials"); let response = self.send(self.client.patch(&url).json(&changes))?; diff --git a/src/mastodon_client.rs b/src/mastodon_client.rs index d183321..1d923a0 100644 --- a/src/mastodon_client.rs +++ b/src/mastodon_client.rs @@ -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 { unimplemented!("This method was not implemented"); } /// PATCH /api/v1/accounts/update_credentials - fn update_credentials(&self, changes: CredentialsBuilder) -> Result { + fn update_credentials(&self, builder: &mut UpdateCredsRequest) -> Result { unimplemented!("This method was not implemented"); } /// POST /api/v1/statuses diff --git a/src/requests/mod.rs b/src/requests/mod.rs index f323806..bb3f1ae 100644 --- a/src/requests/mod.rs +++ b/src/requests/mod.rs @@ -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; diff --git a/src/requests/update_credentials.rs b/src/requests/update_credentials.rs new file mode 100644 index 0000000..52a80df --- /dev/null +++ b/src/requests/update_credentials.rs @@ -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, + note: Option, + avatar: Option, + header: Option, + + // UpdateSource fields + privacy: Option, + sensitive: Option, +} + +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(&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(&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>(&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>(&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 { + 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() + } + ); + } +}