Skip to content

Commit

Permalink
feat!: rework text (#55)
Browse files Browse the repository at this point in the history
This change brings the enum / contents pattern to the Text object, breaking its monolithic declaration into multiple mods and structs. This comes with a host of ergonomics improvements, like lots of handy conversions - for example if there is an fn of type `fn foo(text: impl Into<text::Plain>) -> Foo`, you can invoke it with a `text::Plain`, a string literal, or a `String`.

BREAKING!: `text` is now an enum of single-element tuples. Those tuples point to separate structure definitions for the different kinds of Text objects.

BREAKING!: `text` is now publicly used at the crate root, breaking glob imports like `slack_blocks::*` if there's a local code element named `text`
  • Loading branch information
cakekindel authored Jun 29, 2020
1 parent c6256ba commit 3573f44
Show file tree
Hide file tree
Showing 14 changed files with 382 additions and 310 deletions.
9 changes: 2 additions & 7 deletions src/blocks/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,7 @@ impl Contents {
pub fn from_elements(
elements: impl IntoIterator<Item = block_elements::BlockElement>,
) -> Result<Self, ()> {
elements
.into_iter()
.collect::<Vec<_>>()
.try_into()
elements.into_iter().collect::<Vec<_>>().try_into()
}

/// Populate an Actions block with a collection of `BlockElement`s that
Expand Down Expand Up @@ -146,9 +143,7 @@ impl Contents {
/// // < send block to slack's API >
/// # }
/// ```
pub fn from_action_elements(
elements: impl IntoIterator<Item = self::BlockElement>,
) -> Self {
pub fn from_action_elements(elements: impl IntoIterator<Item = self::BlockElement>) -> Self {
elements
.into_iter()
.map(Into::<self::BlockElement>::into)
Expand Down
19 changes: 2 additions & 17 deletions src/blocks/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,9 @@ use crate::val_helpr::ValidationResult;
/// [context_docs]: https://api.slack.com/reference/block-kit/blocks#context
#[derive(Clone, Debug, Default, Deserialize, Hash, PartialEq, Serialize, Validate)]
pub struct Contents {
/// A collection of [image elements 🔗] and [text objects 🔗].
///
/// Maximum number of items is 10
/// [image elements 🔗]: https://api.slack.com/reference/messaging/block-elements#image
/// [text objects 🔗]: https://api.slack.com/reference/messaging/composition-objects#text
#[validate(length(max = 10))]
elements: Vec<Compose>,

/// A string acting as a unique identifier for a block.
///
/// You can use this `block_id` when you receive an
/// interaction payload to [identify the source of the action 🔗].
///
/// If not specified, a `block_id` will be generated.
///
/// Maximum length for this field is 255 characters.
///
/// [identify the source of the action 🔗]: https://api.slack.com/interactivity/handling#payloads
#[validate(length(max = 255))]
block_id: Option<String>,
}
Expand Down Expand Up @@ -63,10 +48,10 @@ impl Contents {
///
/// ```
/// use slack_blocks::blocks::{Block, context};
/// use slack_blocks::compose;
/// use slack_blocks::compose::text;
///
/// pub fn main() {
/// let text = compose::Text::markdown("*s i c k*");
/// let text = text::Mrkdwn::from("*s i c k*");
/// let context = context::Contents::from_elements(vec![text]);
/// let block: Block = context.into();
/// // < send block to slack's API >
Expand Down
29 changes: 13 additions & 16 deletions src/blocks/image.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::compose;
use crate::compose::text;
use crate::val_helpr::ValidationResult;

/// # Image Block
Expand All @@ -19,9 +19,8 @@ pub struct Contents {
#[validate(length(max = 2000))]
alt_text: String,

#[validate(custom = "compose::validation::text_is_plain")]
#[validate(custom = "validation::text_max_len_2k")]
title: Option<compose::Text>,
#[validate(custom = "validate::title")]
title: Option<text::Text>,

#[validate(length(max = 255))]
block_id: Option<String>,
Expand Down Expand Up @@ -53,7 +52,7 @@ impl Contents {
alt_text: alt_text.to_string(),
image_url: image_url.to_string(),
title: None,
block_id: None
block_id: None,
}
}

Expand All @@ -69,17 +68,16 @@ impl Contents {
/// # Example
/// ```
/// use slack_blocks::blocks::{Block, image};
/// use slack_blocks::compose::Text;
///
/// let url = "https://www.cheese.com/favicon.ico";
/// let image: Block = image::Contents::from_alt_text_and_url("a small image of cheese.", url)
/// .with_title(Text::plain("here is an image of some cheese:"))
/// .with_title("here is an image of some cheese:")
/// .into();
///
/// // < send block to slack's API >
/// ```
pub fn with_title(mut self, title: impl Into<compose::Text>) -> Self {
self.title = Some(title.into());
pub fn with_title(mut self, title: impl Into<text::Plain>) -> Self {
self.title = Some(title.into().into());
self
}

Expand All @@ -97,11 +95,10 @@ impl Contents {
/// # Example
/// ```
/// use slack_blocks::blocks::{Block, image};
/// use slack_blocks::compose::Text;
///
/// let url = "https://www.cheese.com/favicon.ico";
/// let image: Block = image::Contents::from_alt_text_and_url("a small image of cheese.", url)
/// .with_title(Text::plain("here is an image of some cheese:"))
/// .with_title("here is an image of some cheese:")
/// .with_block_id("msg_id_12346")
/// .into();
///
Expand Down Expand Up @@ -142,11 +139,11 @@ impl Contents {
}
}

mod validation {
use crate::compose;
use crate::val_helpr::ValidatorResult;
mod validate {
use crate::compose::text;
use crate::val_helpr::{below_len, ValidatorResult};

pub fn text_max_len_2k(text: &compose::Text) -> ValidatorResult {
compose::validation::text_max_len(text, 2000)
pub fn title(text: &text::Text) -> ValidatorResult {
below_len("Image Title", 2000, text.as_ref())
}
}
75 changes: 29 additions & 46 deletions src/blocks/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::block_elements::select;
use crate::compose;
use crate::compose::text;
use crate::val_helpr::ValidationResult;

/// # Input Block
Expand All @@ -18,16 +18,16 @@ use crate::val_helpr::ValidationResult;
/// [slack's guide to using modals 🔗]: https://api.slack.com/surfaces/modals/using#gathering_input
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize, Validate)]
pub struct Contents {
#[validate(custom = "validation::text_max_len_2k")]
label: compose::Text,
#[validate(custom = "validate::label")]
label: text::Text,

element: InputElement,

#[validate(length(max = 255))]
block_id: Option<String>,

#[validate(custom = "validation::text_max_len_2k")]
hint: Option<compose::Text>,
#[validate(custom = "validate::hint")]
hint: Option<text::Text>,

optional: Option<bool>,
}
Expand All @@ -54,25 +54,20 @@ impl Contents {
/// ```
/// use slack_blocks::block_elements::select;
/// use slack_blocks::blocks;
/// use slack_blocks::compose;
///
/// # use std::error::Error;
/// # pub fn main() -> Result<(), Box<dyn Error>> {
/// let label = compose::Text::plain("On a scale from 1 - 5, how angsty are you?");
/// let label = "On a scale from 1 - 5, how angsty are you?";
/// let input = select::Static {};
///
/// let block = blocks::input::Contents::from_label_and_element(label, input);
///
/// // < send to slack API >
/// # Ok(())
/// # }
/// ```
pub fn from_label_and_element<Label: Into<compose::Text>, El: Into<InputElement>>(
label: Label,
element: El,
pub fn from_label_and_element(
label: impl Into<text::Plain>,
element: impl Into<InputElement>,
) -> Self {
Contents {
label: label.into(),
label: label.into().into(),
element: element.into(),
block_id: None,
hint: None,
Expand All @@ -98,11 +93,8 @@ impl Contents {
/// ```
/// use slack_blocks::block_elements::select;
/// use slack_blocks::blocks;
/// use slack_blocks::compose;
///
/// # use std::error::Error;
/// # pub fn main() -> Result<(), Box<dyn Error>> {
/// let label = compose::Text::plain("On a scale from 1 - 5, how angsty are you?");
/// let label = "On a scale from 1 - 5, how angsty are you?";
/// let input = select::Static {};
///
/// let block = blocks::input
Expand All @@ -111,11 +103,9 @@ impl Contents {
/// .with_block_id("angst_rating_12345");
///
/// // < send to slack API >
/// # Ok(())
/// # }
/// ```
pub fn with_block_id<StrIsh: AsRef<str>>(mut self, block_id: StrIsh) -> Self {
self.block_id = Some(block_id.as_ref().to_string());
pub fn with_block_id(mut self, block_id: impl ToString) -> Self {
self.block_id = Some(block_id.to_string());
self
}

Expand All @@ -135,24 +125,23 @@ impl Contents {
/// ```
/// use slack_blocks::block_elements::select;
/// use slack_blocks::blocks;
/// use slack_blocks::compose;
///
/// # use std::error::Error;
/// # pub fn main() -> Result<(), Box<dyn Error>> {
/// let label = compose::Text::plain("On a scale from 1 - 5, how angsty are you?");
/// let label = "On a scale from 1 - 5, how angsty are you?";
/// let input = select::Static {};
///
/// let block = blocks::input
/// ::Contents
/// ::from_label_and_element(label, input)
/// .with_hint(compose::Text::plain("PSST hey! Don't let them know how angsty you are!"));
/// .with_hint("PSST hey! Don't let them know how angsty you are!");
///
/// // < send to slack API >
/// # Ok(())
/// # }
/// ```
pub fn with_hint<IntoText: Into<compose::Text>>(mut self, hint: IntoText) -> Self {
self.hint = Some(hint.into());
pub fn with_hint(mut self, hint: impl Into<text::Plain>) -> Self {
self.hint = Some(hint.into().into());
self
}

Expand All @@ -167,22 +156,17 @@ impl Contents {
/// ```
/// use slack_blocks::block_elements::select;
/// use slack_blocks::blocks;
/// use slack_blocks::compose;
///
/// # use std::error::Error;
/// # pub fn main() -> Result<(), Box<dyn Error>> {
/// let label = compose::Text::plain("On a scale from 1 - 5, how angsty are you?");
/// let label = "On a scale from 1 - 5, how angsty are you?";
/// let input = select::Static {};
///
/// let block = blocks::input
/// ::Contents
/// ::from_label_and_element(label, input)
/// .with_hint(compose::Text::plain("PSST hey! Don't even answer that!"))
/// .with_hint("PSST hey! Don't even answer that!")
/// .with_optional(true);
///
/// // < send to slack API >
/// # Ok(())
/// # }
/// ```
pub fn with_optional(mut self, optionality: bool) -> Self {
self.optional = Some(optionality);
Expand All @@ -203,11 +187,8 @@ impl Contents {
/// ```
/// use slack_blocks::block_elements::select;
/// use slack_blocks::blocks;
/// use slack_blocks::compose;
///
/// # use std::error::Error;
/// # pub fn main() -> Result<(), Box<dyn Error>> {
/// let label = compose::Text::plain("On a scale from 1 - 5, how angsty are you?");
/// let label = "On a scale from 1 - 5, how angsty are you?";
/// let input = select::Static {};
/// let long_string = std::iter::repeat(' ').take(2001).collect::<String>();
///
Expand All @@ -219,8 +200,6 @@ impl Contents {
/// assert_eq!(true, matches!(block.validate(), Err(_)));
///
/// // < send to slack API >
/// # Ok(())
/// # }
/// ```
pub fn validate(&self) -> ValidationResult {
Validate::validate(self)
Expand Down Expand Up @@ -248,11 +227,15 @@ where
}
}

mod validation {
use crate::compose;
use crate::val_helpr::ValidatorResult;
mod validate {
use crate::compose::text;
use crate::val_helpr::{below_len, ValidatorResult};

pub fn label(text: &text::Text) -> ValidatorResult {
below_len("Input Label", 2000, text.as_ref())
}

pub fn text_max_len_2k(text: &compose::Text) -> ValidatorResult {
compose::validation::text_max_len(text, 2000)
pub fn hint(text: &text::Text) -> ValidatorResult {
below_len("Input Hint", 2000, text.as_ref())
}
}
Loading

0 comments on commit 3573f44

Please sign in to comment.