1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::block::Block::{Actions, Context, Divider, Image, Section};
use serde::Serialize;

pub mod actions;
pub mod context;
pub mod divider;
pub mod image;
pub mod section;

pub(self) const SECTION_TYPE: &str = "section";
pub(self) const ACTIONS_TYPE: &str = "actions";
pub(self) const CONTEXT_TYPE: &str = "context";
pub(self) const DIVIDER_TYPE: &str = "divider";
pub(self) const IMAGE_TYPE: &str = "image";

/// Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages.
///
/// `BlockType` : `Available in surfaces`
/// Actions : Modals, Messages, Home tabs
/// Context : Modals, Messages, Home tabs
/// Divider : Modals, Messages, Home tabs
/// File    : Messages
/// Image   : Modals, Messages, Home tabs
/// Input   : Modals
/// Section : Modals, Messages, Home tabs
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum Block {
    Section(section::SectionBlock),
    Divider(divider::DividerBlock),
    Actions(actions::ActionsBlock),
    Image(image::ImageBlock),
    Context(context::ContextBlock),
    /*    TODO:
     *    File,
     *    Input, */
}

impl From<section::SectionBlock> for Block {
    fn from(block: section::SectionBlock) -> Self {
        Section(block)
    }
}

impl From<divider::DividerBlock> for Block {
    fn from(block: divider::DividerBlock) -> Self {
        Divider(block)
    }
}

impl From<actions::ActionsBlock> for Block {
    fn from(block: actions::ActionsBlock) -> Self {
        Actions(block)
    }
}

impl From<image::ImageBlock> for Block {
    fn from(block: image::ImageBlock) -> Self {
        Image(block)
    }
}

impl From<context::ContextBlock> for Block {
    fn from(block: context::ContextBlock) -> Self {
        Context(block)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::block::actions::ActionsBlock;
    use crate::block::divider::DividerBlock;
    use crate::block::section::SectionBlock;
    use crate::block_element::button::ButtonElement;
    use crate::block_element::BlockElement::Button;
    use crate::composition::text::PlainText;
    use crate::composition::text::Text::Plain;

    #[test]
    fn test_ser_section() {
        let block = Block::Section(SectionBlock::new(Plain(PlainText::new("text"))));
        let json = serde_json::to_string_pretty(&block).unwrap_or("".to_string());
        let expected = r#"{
  "type": "section",
  "text": {
    "type": "plain_text",
    "text": "text"
  }
}"#;
        assert_eq!(json, expected);
    }

    #[test]
    fn test_ser_divider() {
        let block = Block::Divider(DividerBlock::new());
        let json = serde_json::to_string_pretty(&block).unwrap_or("".to_string());
        let expected = r#"{
  "type": "divider"
}"#;
        assert_eq!(json, expected);
    }

    #[test]
    fn test_ser_actions() {
        let block = Block::Actions(ActionsBlock::new(vec![Button(ButtonElement::new(
            PlainText::new("text"),
            "action_id",
        ))]));
        let json = serde_json::to_string_pretty(&block).unwrap_or("".to_string());
        let expected = r#"{
  "type": "actions",
  "elements": [
    {
      "type": "button",
      "text": {
        "type": "plain_text",
        "text": "text"
      },
      "action_id": "action_id"
    }
  ]
}"#;
        assert_eq!(json, expected);
    }
}