summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Woolcock <paul@woolcock.us>2019-05-08 22:07:26 -0400
committerPaul Woolcock <paul@woolcock.us>2019-05-09 23:31:23 -0400
commit174a17109bb445c050f7bd666a2e9df45e69bc2d (patch)
tree66116fef048b6264cdb127a28623fc8d1cd0848b
parent485d25a73208076e7a7a482a562f805bd0f429f4 (diff)
Changes the StatusBuilder to be an...actual...builder
This will enforce the invariant that statuses have to have either status text or a media_id
-rw-r--r--src/lib.rs5
-rw-r--r--src/mastodon_client.rs4
-rw-r--r--src/status_builder.rs282
3 files changed, 240 insertions, 51 deletions
diff --git a/src/lib.rs b/src/lib.rs
index b30b37d..db3bd32 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -127,7 +127,7 @@ pub use requests::{
UpdateCredsRequest,
UpdatePushRequest,
};
-pub use status_builder::StatusBuilder;
+pub use status_builder::{NewStatus, StatusBuilder};
/// Registering your App
pub mod apps;
@@ -160,6 +160,7 @@ pub mod prelude {
pub use Data;
pub use Mastodon;
pub use MastodonClient;
+ pub use NewStatus;
pub use Registration;
pub use StatusBuilder;
pub use StatusesRequest;
@@ -318,7 +319,7 @@ impl<H: HttpSend> MastodonClient<H> for Mastodon<H> {
}
/// Post a new status to the account.
- fn new_status(&self, status: StatusBuilder) -> Result<Status> {
+ fn new_status(&self, status: NewStatus) -> Result<Status> {
let response = self.send(
self.client
.post(&self.route("/api/v1/statuses"))
diff --git a/src/mastodon_client.rs b/src/mastodon_client.rs
index e1f8d01..d1defad 100644
--- a/src/mastodon_client.rs
+++ b/src/mastodon_client.rs
@@ -11,7 +11,7 @@ use requests::{
UpdateCredsRequest,
UpdatePushRequest,
};
-use status_builder::StatusBuilder;
+use status_builder::NewStatus;
/// Represents the set of methods that a Mastodon Client can do, so that
/// implementations might be swapped out for testing
@@ -189,7 +189,7 @@ pub trait MastodonClient<H: HttpSend = HttpSender> {
unimplemented!("This method was not implemented");
}
/// POST /api/v1/statuses
- fn new_status(&self, status: StatusBuilder) -> Result<Status> {
+ fn new_status(&self, status: NewStatus) -> Result<Status> {
unimplemented!("This method was not implemented");
}
/// GET /api/v1/timelines/public
diff --git a/src/status_builder.rs b/src/status_builder.rs
index 06a25ea..e1ce182 100644
--- a/src/status_builder.rs
+++ b/src/status_builder.rs
@@ -1,5 +1,4 @@
use isolang::Language;
-use std::fmt;
/// A builder pattern struct for constructing a status.
///
@@ -9,36 +8,234 @@ use std::fmt;
/// # extern crate elefren;
/// # use elefren::{Language, StatusBuilder};
///
-/// let status = StatusBuilder {
-/// status: "a status".to_string(),
-/// sensitive: Some(true),
-/// spoiler_text: Some("a CW".to_string()),
-/// language: Some(Language::Eng),
-/// ..Default::default()
-/// };
+/// # fn main() -> Result<(), elefren::Error> {
+/// let status = StatusBuilder::new()
+/// .status("a status")
+/// .sensitive(true)
+/// .spoiler_text("a CW")
+/// .language(Language::Eng)
+/// .build()?;
+/// # Ok(())
+/// # }
/// ```
-#[derive(Debug, Default, Clone, Serialize, PartialEq)]
+#[derive(Debug, Default, Clone, PartialEq)]
pub struct StatusBuilder {
- /// The text of the status.
- pub status: String,
- /// Id of status being replied to.
+ status: Option<String>,
+ in_reply_to_id: Option<String>,
+ media_ids: Option<Vec<String>>,
+ sensitive: Option<bool>,
+ spoiler_text: Option<String>,
+ visibility: Option<Visibility>,
+ language: Option<Language>,
+}
+
+impl StatusBuilder {
+ /// Create a StatusBuilder object
+ ///
+ /// # Example
+ ///
+ /// ```rust,no_run
+ /// # use elefren::prelude::*;
+ /// # use elefren::status_builder::Visibility;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// # let data = Data {
+ /// # base: "".into(),
+ /// # client_id: "".into(),
+ /// # client_secret: "".into(),
+ /// # redirect: "".into(),
+ /// # token: "".into(),
+ /// # };
+ /// # let client = Mastodon::from(data);
+ /// let status = StatusBuilder::new()
+ /// .status("a status")
+ /// .visibility(Visibility::Public)
+ /// .build()?;
+ /// client.new_status(status)?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn new() -> StatusBuilder {
+ StatusBuilder::default()
+ }
+
+ /// Set the text for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new().status("awoooooo").build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn status<I: Into<String>>(&mut self, status: I) -> &mut Self {
+ self.status = Some(status.into());
+ self
+ }
+
+ /// Set the in_reply_to_id for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new()
+ /// .status("awooooo")
+ /// .in_reply_to("12345")
+ /// .build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn in_reply_to<I: Into<String>>(&mut self, id: I) -> &mut Self {
+ self.in_reply_to_id = Some(id.into());
+ self
+ }
+
+ /// Set the media_ids for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new().media_ids(&["foo", "bar"]).build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn media_ids<S: std::fmt::Display, I: IntoIterator<Item = S>>(
+ &mut self,
+ ids: I,
+ ) -> &mut Self {
+ self.media_ids = Some(ids.into_iter().map(|s| s.to_string()).collect::<Vec<_>>());
+ self
+ }
+
+ /// Set the sensitive attribute for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new()
+ /// .media_ids(&["foo", "bar"])
+ /// .sensitive(true)
+ /// .build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn sensitive(&mut self, sensitive: bool) -> &mut Self {
+ self.sensitive = Some(sensitive);
+ self
+ }
+
+ /// Set the spoiler text/CW for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new()
+ /// .status("awoooo!!")
+ /// .spoiler_text("awoo inside")
+ /// .build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn spoiler_text<I: Into<String>>(&mut self, spoiler_text: I) -> &mut Self {
+ self.spoiler_text = Some(spoiler_text.into());
+ self
+ }
+
+ /// Set the visibility for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # use elefren::status_builder::Visibility;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new()
+ /// .status("awooooooo")
+ /// .visibility(Visibility::Public)
+ /// .build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn visibility(&mut self, visibility: Visibility) -> &mut Self {
+ self.visibility = Some(visibility);
+ self
+ }
+
+ /// Set the language for the post
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # use elefren::Language;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new()
+ /// .status("awoo!!!!")
+ /// .language(Language::Eng)
+ /// .build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn language(&mut self, language: Language) -> &mut Self {
+ self.language = Some(language);
+ self
+ }
+
+ /// Constructs a NewStatus
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use elefren::prelude::*;
+ /// # fn main() -> Result<(), elefren::Error> {
+ /// let status = StatusBuilder::new().status("awoo!").build()?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn build(&self) -> Result<NewStatus, crate::Error> {
+ if self.status.is_none() && self.media_ids.is_none() {
+ return Err(crate::Error::Other(
+ "status text or media ids are required in order to post a status".to_string(),
+ ));
+ }
+ Ok(NewStatus {
+ status: self.status.clone(),
+ in_reply_to_id: self.in_reply_to_id.clone(),
+ media_ids: self.media_ids.clone(),
+ sensitive: self.sensitive.clone(),
+ spoiler_text: self.spoiler_text.clone(),
+ visibility: self.visibility.clone(),
+ language: self.language.clone(),
+ })
+ }
+}
+
+/// Represents a post that can be sent to the POST /api/v1/status endpoint
+#[derive(Debug, Default, Clone, Serialize, PartialEq)]
+pub struct NewStatus {
+ #[serde(skip_serializing_if = "Option::is_none")]
+ status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub in_reply_to_id: Option<String>,
- /// Ids of media attachments being attached to the status.
+ in_reply_to_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub media_ids: Option<Vec<String>>,
- /// Whether current status is sensitive.
+ media_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub sensitive: Option<bool>,
- /// Text to precede the normal status text.
+ sensitive: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub spoiler_text: Option<String>,
- /// Visibility of the status, defaults to `Public`.
+ spoiler_text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub visibility: Option<Visibility>,
- /// Language code of the status
+ visibility: Option<Visibility>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub language: Option<Language>,
+ language: Option<Language>,
}
/// The visibility of a status.
@@ -55,21 +252,6 @@ pub enum Visibility {
Public,
}
-impl StatusBuilder {
- /// Create a new status with text.
- /// ```
- /// use elefren::prelude::*;
- ///
- /// let status = StatusBuilder::new("Hello World!");
- /// ```
- pub fn new<D: fmt::Display>(status: D) -> Self {
- StatusBuilder {
- status: status.to_string(),
- ..Self::default()
- }
- }
-}
-
impl Default for Visibility {
fn default() -> Self {
Visibility::Public
@@ -84,9 +266,12 @@ mod tests {
#[test]
fn test_new() {
- let s = StatusBuilder::new("a status");
- let expected = StatusBuilder {
- status: "a status".to_string(),
+ let s = StatusBuilder::new()
+ .status("a status")
+ .build()
+ .expect("Couldn't build status");
+ let expected = NewStatus {
+ status: Some("a status".to_string()),
in_reply_to_id: None,
media_ids: None,
sensitive: None,
@@ -125,17 +310,20 @@ mod tests {
#[test]
fn test_serialize_status() {
- let status = StatusBuilder::new("a status");
+ let status = StatusBuilder::new()
+ .status("a status")
+ .build()
+ .expect("Couldn't build status");
assert_eq!(
serde_json::to_string(&status).expect("Couldn't serialize status"),
"{\"status\":\"a status\"}".to_string()
);
- let status = StatusBuilder {
- status: "a status".into(),
- language: Some(Language::Eng),
- ..Default::default()
- };
+ let status = StatusBuilder::new()
+ .status("a status")
+ .language(Language::Eng)
+ .build()
+ .expect("Couldn't build status");
assert_eq!(
serde_json::to_string(&status).expect("Couldn't serialize status"),
"{\"status\":\"a status\",\"language\":\"eng\"}"