@@ -34,9 +34,8 @@ pub enum SlackBlock {
3434 #[ serde( rename = "markdown" ) ]
3535 Markdown ( SlackMarkdownBlock ) ,
3636
37- // This block is still undocumented, so we don't define any structure yet we can return it back,
3837 #[ serde( rename = "rich_text" ) ]
39- RichText ( serde_json :: Value ) ,
38+ RichText ( SlackRichTextBlock ) ,
4039 #[ serde( rename = "share_shortcut" ) ]
4140 ShareShortcut ( serde_json:: Value ) ,
4241 #[ serde( rename = "event" ) ]
@@ -291,6 +290,8 @@ pub enum SlackInputBlockElement {
291290 Checkboxes ( SlackBlockCheckboxesElement ) ,
292291 #[ serde( rename = "email_text_input" ) ]
293292 EmailInput ( SlackBlockEmailInputElement ) ,
293+ #[ serde( rename = "rich_text_input" ) ]
294+ RichTextInput ( SlackBlockRichTextInputElement ) ,
294295}
295296
296297#[ skip_serializing_none]
@@ -1070,6 +1071,202 @@ impl From<SlackMarkdownBlock> for SlackBlock {
10701071 }
10711072}
10721073
1074+ /**
1075+ * https://api.slack.com/reference/block-kit/blocks#rich_text
1076+ */
1077+ #[ skip_serializing_none]
1078+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1079+ pub struct SlackRichTextBlock {
1080+ pub block_id : Option < SlackBlockId > ,
1081+ pub elements : Vec < SlackRichTextElement > ,
1082+ }
1083+
1084+ impl From < SlackRichTextBlock > for SlackBlock {
1085+ fn from ( block : SlackRichTextBlock ) -> Self {
1086+ SlackBlock :: RichText ( block)
1087+ }
1088+ }
1089+
1090+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
1091+ #[ serde( tag = "type" ) ]
1092+ pub enum SlackRichTextElement {
1093+ #[ serde( rename = "rich_text_section" ) ]
1094+ Section ( SlackRichTextSection ) ,
1095+ #[ serde( rename = "rich_text_list" ) ]
1096+ List ( SlackRichTextList ) ,
1097+ #[ serde( rename = "rich_text_preformatted" ) ]
1098+ Preformatted ( SlackRichTextPreformatted ) ,
1099+ #[ serde( rename = "rich_text_quote" ) ]
1100+ Quote ( SlackRichTextQuote ) ,
1101+ }
1102+
1103+ #[ skip_serializing_none]
1104+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1105+ pub struct SlackRichTextSection {
1106+ pub elements : Vec < SlackRichTextInlineElement > ,
1107+ }
1108+
1109+ #[ skip_serializing_none]
1110+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1111+ pub struct SlackRichTextList {
1112+ pub style : SlackRichTextListStyle ,
1113+ pub elements : Vec < SlackRichTextSection > ,
1114+ pub indent : Option < u64 > ,
1115+ pub offset : Option < u64 > ,
1116+ pub border : Option < u64 > ,
1117+ }
1118+
1119+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
1120+ #[ serde( rename_all = "snake_case" ) ]
1121+ pub enum SlackRichTextListStyle {
1122+ Bullet ,
1123+ Ordered ,
1124+ }
1125+
1126+ #[ skip_serializing_none]
1127+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1128+ pub struct SlackRichTextPreformatted {
1129+ pub elements : Vec < SlackRichTextInlineElement > ,
1130+ pub border : Option < u64 > ,
1131+ pub language : Option < String > ,
1132+ }
1133+
1134+ #[ skip_serializing_none]
1135+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1136+ pub struct SlackRichTextQuote {
1137+ pub elements : Vec < SlackRichTextInlineElement > ,
1138+ pub border : Option < u64 > ,
1139+ }
1140+
1141+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
1142+ #[ serde( tag = "type" ) ]
1143+ pub enum SlackRichTextInlineElement {
1144+ #[ serde( rename = "text" ) ]
1145+ Text ( SlackRichTextText ) ,
1146+ #[ serde( rename = "link" ) ]
1147+ Link ( SlackRichTextLink ) ,
1148+ #[ serde( rename = "user" ) ]
1149+ User ( SlackRichTextUser ) ,
1150+ #[ serde( rename = "channel" ) ]
1151+ Channel ( SlackRichTextChannel ) ,
1152+ #[ serde( rename = "usergroup" ) ]
1153+ UserGroup ( SlackRichTextUserGroup ) ,
1154+ #[ serde( rename = "emoji" ) ]
1155+ Emoji ( SlackRichTextEmoji ) ,
1156+ #[ serde( rename = "date" ) ]
1157+ Date ( SlackRichTextDate ) ,
1158+ #[ serde( rename = "broadcast" ) ]
1159+ Broadcast ( SlackRichTextBroadcast ) ,
1160+ #[ serde( rename = "color" ) ]
1161+ Color ( SlackRichTextColor ) ,
1162+ }
1163+
1164+ #[ skip_serializing_none]
1165+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1166+ pub struct SlackRichTextStyle {
1167+ pub bold : Option < bool > ,
1168+ pub italic : Option < bool > ,
1169+ pub strike : Option < bool > ,
1170+ pub code : Option < bool > ,
1171+ pub underline : Option < bool > ,
1172+ pub highlight : Option < bool > ,
1173+ pub client_highlight : Option < bool > ,
1174+ pub unlink : Option < bool > ,
1175+ }
1176+
1177+ #[ skip_serializing_none]
1178+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1179+ pub struct SlackRichTextText {
1180+ pub text : String ,
1181+ pub style : Option < SlackRichTextStyle > ,
1182+ }
1183+
1184+ #[ skip_serializing_none]
1185+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1186+ pub struct SlackRichTextLink {
1187+ pub url : String ,
1188+ pub text : Option < String > ,
1189+ #[ serde( rename = "unsafe" ) ]
1190+ pub unsafe_ : Option < bool > ,
1191+ pub style : Option < SlackRichTextStyle > ,
1192+ }
1193+
1194+ #[ skip_serializing_none]
1195+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1196+ pub struct SlackRichTextUser {
1197+ pub user_id : SlackUserId ,
1198+ pub style : Option < SlackRichTextStyle > ,
1199+ }
1200+
1201+ #[ skip_serializing_none]
1202+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1203+ pub struct SlackRichTextChannel {
1204+ pub channel_id : SlackChannelId ,
1205+ pub style : Option < SlackRichTextStyle > ,
1206+ }
1207+
1208+ #[ skip_serializing_none]
1209+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1210+ pub struct SlackRichTextUserGroup {
1211+ pub usergroup_id : SlackUserGroupId ,
1212+ pub style : Option < SlackRichTextStyle > ,
1213+ }
1214+
1215+ #[ skip_serializing_none]
1216+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1217+ pub struct SlackRichTextEmoji {
1218+ pub name : SlackEmojiName ,
1219+ pub unicode : Option < String > ,
1220+ }
1221+
1222+ #[ skip_serializing_none]
1223+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1224+ pub struct SlackRichTextDate {
1225+ pub timestamp : i64 ,
1226+ pub format : String ,
1227+ pub fallback : Option < String > ,
1228+ pub style : Option < SlackRichTextStyle > ,
1229+ }
1230+
1231+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
1232+ #[ serde( rename_all = "snake_case" ) ]
1233+ pub enum SlackRichTextBroadcastRange {
1234+ Here ,
1235+ Channel ,
1236+ Everyone ,
1237+ }
1238+
1239+ #[ skip_serializing_none]
1240+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1241+ pub struct SlackRichTextBroadcast {
1242+ pub range : SlackRichTextBroadcastRange ,
1243+ pub style : Option < SlackRichTextStyle > ,
1244+ }
1245+
1246+ #[ skip_serializing_none]
1247+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1248+ pub struct SlackRichTextColor {
1249+ pub value : String ,
1250+ }
1251+
1252+ /**
1253+ * https://api.slack.com/reference/block-kit/block-elements#rich_text_input
1254+ */
1255+ #[ skip_serializing_none]
1256+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize , Builder ) ]
1257+ pub struct SlackBlockRichTextInputElement {
1258+ pub action_id : SlackActionId ,
1259+ pub initial_value : Option < SlackRichTextBlock > ,
1260+ pub focus_on_load : Option < bool > ,
1261+ pub placeholder : Option < SlackBlockPlainTextOnly > ,
1262+ }
1263+
1264+ impl From < SlackBlockRichTextInputElement > for SlackInputBlockElement {
1265+ fn from ( element : SlackBlockRichTextInputElement ) -> Self {
1266+ SlackInputBlockElement :: RichTextInput ( element)
1267+ }
1268+ }
1269+
10731270#[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
10741271#[ serde( untagged) ]
10751272pub enum SlackImageUrlOrFile {
@@ -1222,4 +1419,75 @@ mod test {
12221419 }
12231420 Ok ( ( ) )
12241421 }
1422+
1423+ #[ test]
1424+ fn test_rich_text_block_deserialize ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
1425+ let payload = include_str ! ( "./fixtures/slack_rich_text_block.json" ) ;
1426+ let block: SlackBlock = serde_json:: from_str ( payload) ?;
1427+
1428+ let rich = match block {
1429+ SlackBlock :: RichText ( r) => r,
1430+ _ => panic ! ( "Expected a RichText block" ) ,
1431+ } ;
1432+
1433+ assert_eq ! ( rich. block_id, Some ( SlackBlockId ( "test_block" . into( ) ) ) ) ;
1434+ assert_eq ! ( rich. elements. len( ) , 4 ) ;
1435+
1436+ // section
1437+ let section = match & rich. elements [ 0 ] {
1438+ SlackRichTextElement :: Section ( s) => s,
1439+ _ => panic ! ( "Expected a Section element" ) ,
1440+ } ;
1441+ assert_eq ! ( section. elements. len( ) , 7 ) ;
1442+
1443+ // bold text
1444+ let text = match & section. elements [ 0 ] {
1445+ SlackRichTextInlineElement :: Text ( t) => t,
1446+ _ => panic ! ( "Expected a Text element" ) ,
1447+ } ;
1448+ assert_eq ! ( text. text, "Hello " ) ;
1449+ assert_eq ! ( text. style. as_ref( ) . and_then( |s| s. bold) , Some ( true ) ) ;
1450+
1451+ // user
1452+ assert ! ( matches!(
1453+ & section. elements[ 1 ] ,
1454+ SlackRichTextInlineElement :: User ( _)
1455+ ) ) ;
1456+
1457+ // emoji — name should deserialize as SlackEmojiName
1458+ let emoji = match & section. elements [ 4 ] {
1459+ SlackRichTextInlineElement :: Emoji ( e) => e,
1460+ _ => panic ! ( "Expected an Emoji element" ) ,
1461+ } ;
1462+ assert_eq ! ( emoji. name, SlackEmojiName :: new( "wave" . into( ) ) ) ;
1463+
1464+ // list
1465+ let list = match & rich. elements [ 1 ] {
1466+ SlackRichTextElement :: List ( l) => l,
1467+ _ => panic ! ( "Expected a List element" ) ,
1468+ } ;
1469+ assert_eq ! ( list. style, SlackRichTextListStyle :: Bullet ) ;
1470+ assert_eq ! ( list. elements. len( ) , 2 ) ;
1471+
1472+ // preformatted
1473+ assert ! ( matches!(
1474+ & rich. elements[ 2 ] ,
1475+ SlackRichTextElement :: Preformatted ( _)
1476+ ) ) ;
1477+
1478+ // quote
1479+ assert ! ( matches!( & rich. elements[ 3 ] , SlackRichTextElement :: Quote ( _) ) ) ;
1480+
1481+ Ok ( ( ) )
1482+ }
1483+
1484+ #[ test]
1485+ fn test_rich_text_block_roundtrip ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
1486+ let payload = include_str ! ( "./fixtures/slack_rich_text_block.json" ) ;
1487+ let block: SlackBlock = serde_json:: from_str ( payload) ?;
1488+ let serialized = serde_json:: to_string ( & block) ?;
1489+ let block2: SlackBlock = serde_json:: from_str ( & serialized) ?;
1490+ assert_eq ! ( block, block2) ;
1491+ Ok ( ( ) )
1492+ }
12251493}
0 commit comments