1use slack_morphism::prelude::*;
2
3use crate::{
4 references::SlackReferences,
5 visitor::{
6 visit_slack_block_mark_down_text, visit_slack_block_plain_text, visit_slack_context_block,
7 visit_slack_divider_block, visit_slack_header_block, visit_slack_markdown_block,
8 visit_slack_section_block, visit_slack_video_block, SlackRichTextBlock, Visitor,
9 },
10};
11
12pub fn render_blocks_as_text(blocks: Vec<SlackBlock>, slack_references: SlackReferences) -> String {
15 let mut block_renderer = TextRenderer::new(slack_references);
16 for block in blocks {
17 block_renderer.visit_slack_block(&block);
18 }
19 block_renderer.sub_texts.join("")
20}
21
22struct TextRenderer {
23 pub sub_texts: Vec<String>,
24 pub slack_references: SlackReferences,
25}
26
27impl TextRenderer {
28 pub fn new(slack_references: SlackReferences) -> Self {
29 TextRenderer {
30 sub_texts: vec![],
31 slack_references,
32 }
33 }
34}
35
36impl Visitor for TextRenderer {
37 fn visit_slack_section_block(&mut self, slack_section_block: &SlackSectionBlock) {
38 let mut section_renderer = TextRenderer::new(self.slack_references.clone());
39 visit_slack_section_block(&mut section_renderer, slack_section_block);
40 self.sub_texts.push(section_renderer.sub_texts.join(""));
41 }
42
43 fn visit_slack_block_plain_text(&mut self, slack_block_plain_text: &SlackBlockPlainText) {
44 self.sub_texts.push(slack_block_plain_text.text.clone());
45 visit_slack_block_plain_text(self, slack_block_plain_text);
46 }
47
48 fn visit_slack_header_block(&mut self, slack_header_block: &SlackHeaderBlock) {
49 let mut header_renderer = TextRenderer::new(self.slack_references.clone());
50 visit_slack_header_block(&mut header_renderer, slack_header_block);
51 self.sub_texts.push(header_renderer.sub_texts.join(""));
52 }
53
54 fn visit_slack_divider_block(&mut self, slack_divider_block: &SlackDividerBlock) {
55 self.sub_texts.push("---\n".to_string());
56 visit_slack_divider_block(self, slack_divider_block);
57 }
58
59 fn visit_slack_block_mark_down_text(
60 &mut self,
61 slack_block_mark_down_text: &SlackBlockMarkDownText,
62 ) {
63 self.sub_texts.push(slack_block_mark_down_text.text.clone());
64 visit_slack_block_mark_down_text(self, slack_block_mark_down_text);
65 }
66
67 fn visit_slack_context_block(&mut self, slack_context_block: &SlackContextBlock) {
68 let mut section_renderer = TextRenderer::new(self.slack_references.clone());
69 visit_slack_context_block(&mut section_renderer, slack_context_block);
70 self.sub_texts.push(section_renderer.sub_texts.join(""));
71 }
72
73 fn visit_slack_rich_text_block(&mut self, slack_rich_text_block: &SlackRichTextBlock) {
74 self.sub_texts.push(render_rich_text_block_as_text(
75 slack_rich_text_block.json_value.clone(),
76 &self.slack_references,
77 ));
78 }
79
80 fn visit_slack_video_block(&mut self, slack_video_block: &SlackVideoBlock) {
81 let title: SlackBlockText = slack_video_block.title.clone().into();
82 let title = match title {
83 SlackBlockText::Plain(plain_text) => plain_text.text,
84 SlackBlockText::MarkDown(md_text) => md_text.text,
85 };
86 self.sub_texts.push(title);
87
88 if let Some(description) = slack_video_block.description.clone() {
89 let description: SlackBlockText = description.into();
90 let description = match description {
91 SlackBlockText::Plain(plain_text) => plain_text.text,
92 SlackBlockText::MarkDown(md_text) => md_text.text,
93 };
94 self.sub_texts.push(format!("\n{}", description));
95 }
96
97 visit_slack_video_block(self, slack_video_block);
98 }
99
100 fn visit_slack_markdown_block(&mut self, slack_markdown_block: &SlackMarkdownBlock) {
101 self.sub_texts.push(slack_markdown_block.text.clone());
102 visit_slack_markdown_block(self, slack_markdown_block);
103 }
104}
105
106fn render_rich_text_block_as_text(
107 json_value: serde_json::Value,
108 slack_references: &SlackReferences,
109) -> String {
110 match json_value.get("elements") {
111 Some(serde_json::Value::Array(elements)) => elements
112 .iter()
113 .map(|element| {
114 match (
115 element.get("type").map(|t| t.as_str()),
116 element.get("style"),
117 element.get("elements"),
118 ) {
119 (
120 Some(Some("rich_text_section")),
121 None,
122 Some(serde_json::Value::Array(elements)),
123 ) => render_rich_text_section_elements(elements, slack_references),
124 (
125 Some(Some("rich_text_list")),
126 Some(serde_json::Value::String(style)),
127 Some(serde_json::Value::Array(elements)),
128 ) => render_rich_text_list_elements(elements, style, slack_references),
129 (
130 Some(Some("rich_text_preformatted")),
131 None,
132 Some(serde_json::Value::Array(elements)),
133 ) => render_rich_text_preformatted_elements(elements, slack_references),
134 (
135 Some(Some("rich_text_quote")),
136 None,
137 Some(serde_json::Value::Array(elements)),
138 ) => render_rich_text_quote_elements(elements, slack_references),
139 _ => "".to_string(),
140 }
141 })
142 .collect::<Vec<String>>()
143 .join(""),
144 _ => "".to_string(),
145 }
146}
147
148fn render_rich_text_section_elements(
149 elements: &[serde_json::Value],
150 slack_references: &SlackReferences,
151) -> String {
152 elements
153 .iter()
154 .map(|e| render_rich_text_section_element(e, slack_references))
155 .collect::<Vec<String>>()
156 .join("")
157}
158
159fn render_rich_text_list_elements(
160 elements: &[serde_json::Value],
161 style: &str,
162 slack_references: &SlackReferences,
163) -> String {
164 let list_style = if style == "ordered" { "1." } else { "-" };
165 elements
166 .iter()
167 .filter_map(|element| {
168 if let Some(serde_json::Value::Array(elements)) = element.get("elements") {
169 Some(render_rich_text_section_elements(
170 elements,
171 slack_references,
172 ))
173 } else {
174 None
175 }
176 })
177 .map(|element| format!("{list_style} {element}"))
178 .collect::<Vec<String>>()
179 .join("\n")
180}
181
182fn render_rich_text_preformatted_elements(
183 elements: &[serde_json::Value],
184 slack_references: &SlackReferences,
185) -> String {
186 render_rich_text_section_elements(elements, slack_references)
187}
188
189fn render_rich_text_quote_elements(
190 elements: &[serde_json::Value],
191 slack_references: &SlackReferences,
192) -> String {
193 render_rich_text_section_elements(elements, slack_references)
194}
195
196fn render_rich_text_section_element(
197 element: &serde_json::Value,
198 slack_references: &SlackReferences,
199) -> String {
200 match element.get("type").map(|t| t.as_str()) {
201 Some(Some("text")) => {
202 let Some(serde_json::Value::String(text)) = element.get("text") else {
203 return "".to_string();
204 };
205 text.to_string()
206 }
207 Some(Some("channel")) => {
208 let Some(serde_json::Value::String(channel_id)) = element.get("channel_id") else {
209 return "".to_string();
210 };
211 let channel_rendered = if let Some(Some(channel_name)) = slack_references
212 .channels
213 .get(&SlackChannelId(channel_id.clone()))
214 {
215 channel_name
216 } else {
217 channel_id
218 };
219 format!("#{channel_rendered}")
220 }
221 Some(Some("user")) => {
222 let Some(serde_json::Value::String(user_id)) = element.get("user_id") else {
223 return "".to_string();
224 };
225 let user_rendered = if let Some(Some(user_name)) =
226 slack_references.users.get(&SlackUserId(user_id.clone()))
227 {
228 user_name
229 } else {
230 user_id
231 };
232 format!("@{user_rendered}")
233 }
234 Some(Some("usergroup")) => {
235 let Some(serde_json::Value::String(usergroup_id)) = element.get("usergroup_id") else {
236 return "".to_string();
237 };
238 let usergroup_rendered = if let Some(Some(usergroup_name)) = slack_references
239 .usergroups
240 .get(&SlackUserGroupId(usergroup_id.clone()))
241 {
242 usergroup_name
243 } else {
244 usergroup_id
245 };
246 format!("@{usergroup_rendered}")
247 }
248 Some(Some("emoji")) => {
249 let Some(serde_json::Value::String(name)) = element.get("name") else {
250 return "".to_string();
251 };
252 let splitted = name.split("::skin-tone-").collect::<Vec<&str>>();
253 let Some(first) = splitted.first() else {
254 return "".to_string();
255 };
256 let Some(emoji) = emojis::get_by_shortcode(first) else {
257 return "".to_string();
258 };
259 let Some(skin_tone) = splitted.get(1).and_then(|s| s.parse::<usize>().ok()) else {
260 return emoji.to_string();
261 };
262 let Some(mut skin_tones) = emoji.skin_tones() else {
263 return emoji.to_string();
264 };
265 let Some(skinned_emoji) = skin_tones.nth(skin_tone - 1) else {
266 return emoji.to_string();
267 };
268 skinned_emoji.to_string()
269 }
270 Some(Some("link")) => {
271 let Some(serde_json::Value::String(text)) = element.get("text") else {
272 return "".to_string();
273 };
274 text.to_string()
275 }
276 _ => "".to_string(),
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use std::collections::HashMap;
283
284 use url::Url;
285
286 use super::*;
287 use crate::test_utils::rich_text_block;
288
289 #[test]
290 fn test_empty_input() {
291 assert_eq!(
292 render_blocks_as_text(vec![], SlackReferences::default()),
293 "".to_string()
294 );
295 }
296
297 #[test]
298 fn test_with_image() {
299 let blocks = vec![
300 SlackBlock::Image(SlackImageBlock::new(
301 SlackImageUrlOrFile::ImageUrl {
302 image_url: Url::parse("https://example.com/image.png").unwrap(),
303 },
304 "Image".to_string(),
305 )),
306 SlackBlock::Image(SlackImageBlock::new(
307 SlackImageUrlOrFile::ImageUrl {
308 image_url: Url::parse("https://example.com/image2.png").unwrap(),
309 },
310 "Image2".to_string(),
311 )),
312 ];
313 assert_eq!(
314 render_blocks_as_text(blocks, SlackReferences::default()),
315 "".to_string()
316 );
317 }
318
319 #[test]
320 fn test_with_divider() {
321 let blocks = vec![
322 SlackBlock::Divider(SlackDividerBlock::new()),
323 SlackBlock::Divider(SlackDividerBlock::new()),
324 ];
325 assert_eq!(
326 render_blocks_as_text(blocks, SlackReferences::default()),
327 "---\n---\n".to_string()
328 );
329 }
330
331 #[test]
332 fn test_with_input() {
333 let blocks = vec![SlackBlock::Input(SlackInputBlock::new(
335 "label".into(),
336 SlackInputBlockElement::PlainTextInput(SlackBlockPlainTextInputElement::new(
337 "id".into(),
338 )),
339 ))];
340 assert_eq!(
341 render_blocks_as_text(blocks, SlackReferences::default()),
342 "".to_string()
343 );
344 }
345
346 #[test]
347 fn test_with_action() {
348 let blocks = vec![SlackBlock::Actions(SlackActionsBlock::new(vec![]))];
350 assert_eq!(
351 render_blocks_as_text(blocks, SlackReferences::default()),
352 "".to_string()
353 );
354 }
355
356 #[test]
357 fn test_with_file() {
358 let blocks = vec![SlackBlock::File(SlackFileBlock::new("external_id".into()))];
360 assert_eq!(
361 render_blocks_as_text(blocks, SlackReferences::default()),
362 "".to_string()
363 );
364 }
365
366 #[test]
367 fn test_with_video() {
368 let blocks = vec![SlackBlock::Video(
369 SlackVideoBlock::new(
370 "alt text".into(),
371 "Video title".into(),
372 "https://example.com/thumbnail.jpg".parse().unwrap(),
373 "https://example.com/video_embed.avi".parse().unwrap(),
374 )
375 .with_description("Video description".into())
376 .with_title_url("https://example.com/video".parse().unwrap()),
377 )];
378 assert_eq!(
379 render_blocks_as_text(blocks, SlackReferences::default()),
380 r#"Video title
381Video description"#
382 .to_string()
383 );
384 }
385
386 #[test]
387 fn test_with_video_minimal() {
388 let blocks = vec![SlackBlock::Video(SlackVideoBlock::new(
389 "alt text".into(),
390 "Video title".into(),
391 "https://example.com/thumbnail.jpg".parse().unwrap(),
392 "https://example.com/video_embed.avi".parse().unwrap(),
393 ))];
394 assert_eq!(
395 render_blocks_as_text(blocks, SlackReferences::default()),
396 "Video title".to_string()
397 );
398 }
399
400 #[test]
401 fn test_with_event() {
402 let blocks = vec![SlackBlock::Event(serde_json::json!({}))];
404 assert_eq!(
405 render_blocks_as_text(blocks, SlackReferences::default()),
406 "".to_string()
407 );
408 }
409
410 #[test]
411 fn test_header() {
412 let blocks = vec![SlackBlock::Header(SlackHeaderBlock::new("Text".into()))];
413 assert_eq!(
414 render_blocks_as_text(blocks, SlackReferences::default()),
415 "Text".to_string()
416 );
417 }
418
419 mod section {
420 use super::*;
421
422 #[test]
423 fn test_with_plain_text() {
424 let blocks = vec![
425 SlackBlock::Section(SlackSectionBlock::new().with_text(SlackBlockText::Plain(
426 SlackBlockPlainText::new("Text".to_string()),
427 ))),
428 SlackBlock::Section(SlackSectionBlock::new().with_text(SlackBlockText::Plain(
429 SlackBlockPlainText::new("Text2".to_string()),
430 ))),
431 ];
432 assert_eq!(
433 render_blocks_as_text(blocks, SlackReferences::default()),
434 "TextText2".to_string()
435 );
436 }
437
438 #[test]
439 fn test_with_markdown() {
440 let blocks = vec![
441 SlackBlock::Section(SlackSectionBlock::new().with_text(SlackBlockText::MarkDown(
442 SlackBlockMarkDownText::new("Text".to_string()),
443 ))),
444 SlackBlock::Section(SlackSectionBlock::new().with_text(SlackBlockText::MarkDown(
445 SlackBlockMarkDownText::new("Text2".to_string()),
446 ))),
447 ];
448 assert_eq!(
449 render_blocks_as_text(blocks, SlackReferences::default()),
450 "TextText2".to_string()
451 );
452 }
453
454 #[test]
455 fn test_with_fields() {
456 let blocks = vec![
457 SlackBlock::Section(SlackSectionBlock::new().with_fields(vec![
458 SlackBlockText::Plain(SlackBlockPlainText::new("Text11".to_string())),
459 SlackBlockText::Plain(SlackBlockPlainText::new("Text12".to_string())),
460 ])),
461 SlackBlock::Section(SlackSectionBlock::new().with_fields(vec![
462 SlackBlockText::Plain(SlackBlockPlainText::new("Text21".to_string())),
463 SlackBlockText::Plain(SlackBlockPlainText::new("Text22".to_string())),
464 ])),
465 ];
466 assert_eq!(
467 render_blocks_as_text(blocks, SlackReferences::default()),
468 "Text11Text12Text21Text22".to_string()
469 );
470 }
471
472 #[test]
473 fn test_with_fields_and_text() {
474 let blocks = vec![
475 SlackBlock::Section(
476 SlackSectionBlock::new()
477 .with_text(SlackBlockText::MarkDown(SlackBlockMarkDownText::new(
478 "Text1".to_string(),
479 )))
480 .with_fields(vec![
481 SlackBlockText::Plain(SlackBlockPlainText::new("Text11".to_string())),
482 SlackBlockText::Plain(SlackBlockPlainText::new("Text12".to_string())),
483 ]),
484 ),
485 SlackBlock::Section(
486 SlackSectionBlock::new()
487 .with_text(SlackBlockText::MarkDown(SlackBlockMarkDownText::new(
488 "Text2".to_string(),
489 )))
490 .with_fields(vec![
491 SlackBlockText::Plain(SlackBlockPlainText::new("Text21".to_string())),
492 SlackBlockText::Plain(SlackBlockPlainText::new("Text22".to_string())),
493 ]),
494 ),
495 ];
496 assert_eq!(
497 render_blocks_as_text(blocks, SlackReferences::default()),
498 "Text1Text11Text12Text2Text21Text22".to_string()
499 );
500 }
501 }
502
503 mod context {
504 use super::*;
505
506 #[test]
507 fn test_with_image() {
508 let blocks = vec![SlackBlock::Context(SlackContextBlock::new(vec![
509 SlackContextBlockElement::Image(SlackBlockImageElement::new(
510 SlackImageUrlOrFile::ImageUrl {
511 image_url: Url::parse("https://example.com/image.png").unwrap(),
512 },
513 "Image".to_string(),
514 )),
515 SlackContextBlockElement::Image(SlackBlockImageElement::new(
516 SlackImageUrlOrFile::ImageUrl {
517 image_url: Url::parse("https://example.com/image2.png").unwrap(),
518 },
519 "Image2".to_string(),
520 )),
521 ]))];
522 assert_eq!(
523 render_blocks_as_text(blocks, SlackReferences::default()),
524 "".to_string()
525 );
526 }
527
528 #[test]
529 fn test_with_plain_text() {
530 let blocks = vec![SlackBlock::Context(SlackContextBlock::new(vec![
531 SlackContextBlockElement::Plain(SlackBlockPlainText::new("Text".to_string())),
532 SlackContextBlockElement::Plain(SlackBlockPlainText::new("Text2".to_string())),
533 ]))];
534 assert_eq!(
535 render_blocks_as_text(blocks, SlackReferences::default()),
536 "TextText2".to_string()
537 );
538 }
539
540 #[test]
541 fn test_with_markdown() {
542 let blocks = vec![SlackBlock::Context(SlackContextBlock::new(vec![
543 SlackContextBlockElement::MarkDown(SlackBlockMarkDownText::new("Text".to_string())),
544 SlackContextBlockElement::MarkDown(SlackBlockMarkDownText::new(
545 "Text2".to_string(),
546 )),
547 ]))];
548 assert_eq!(
549 render_blocks_as_text(blocks, SlackReferences::default()),
550 "TextText2".to_string()
551 );
552 }
553 }
554
555 mod rich_text {
556 use super::*;
557
558 #[test]
559 fn test_with_empty_json() {
560 let blocks = vec![
561 rich_text_block(serde_json::json!({})),
562 rich_text_block(serde_json::json!({})),
563 ];
564 assert_eq!(
565 render_blocks_as_text(blocks, SlackReferences::default()),
566 "".to_string()
567 );
568 }
569
570 mod rich_text_section {
571 use super::*;
572
573 mod text_element {
574 use super::*;
575
576 #[test]
577 fn test_with_text() {
578 let blocks = vec![
579 rich_text_block(serde_json::json!({
580 "type": "rich_text",
581 "elements": [
582 {
583 "type": "rich_text_section",
584 "elements": [
585 {
586 "type": "text",
587 "text": "Text111"
588 },
589 {
590 "type": "text",
591 "text": "Text112"
592 }
593 ]
594 },
595 {
596 "type": "rich_text_section",
597 "elements": [
598 {
599 "type": "text",
600 "text": "Text121"
601 },
602 {
603 "type": "text",
604 "text": "Text122"
605 }
606 ]
607 }
608 ]
609 })),
610 rich_text_block(serde_json::json!({
611 "type": "rich_text",
612 "elements": [
613 {
614 "type": "rich_text_section",
615 "elements": [
616 {
617 "type": "text",
618 "text": "Text211"
619 },
620 {
621 "type": "text",
622 "text": "Text212"
623 }
624 ]
625 },
626 {
627 "type": "rich_text_section",
628 "elements": [
629 {
630 "type": "text",
631 "text": "Text221"
632 },
633 {
634 "type": "text",
635 "text": "Text222"
636 }
637 ]
638 }
639 ]
640 })),
641 ];
642 assert_eq!(
643 render_blocks_as_text(blocks, SlackReferences::default()),
644 "Text111Text112Text121Text122Text211Text212Text221Text222".to_string()
645 );
646 }
647
648 #[test]
649 fn test_with_bold_text() {
650 let blocks = vec![rich_text_block(serde_json::json!({
651 "type": "rich_text",
652 "elements": [
653 {
654 "type": "rich_text_section",
655 "elements": [
656 {
657 "type": "text",
658 "text": "Text",
659 "style": {
660 "bold": true
661 }
662 }
663 ]
664 }
665 ]
666 }))];
667 assert_eq!(
668 render_blocks_as_text(blocks, SlackReferences::default()),
669 "Text".to_string()
670 );
671 }
672
673 #[test]
674 fn test_with_italic_text() {
675 let blocks = vec![rich_text_block(serde_json::json!({
676 "type": "rich_text",
677 "elements": [
678 {
679 "type": "rich_text_section",
680 "elements": [
681 {
682 "type": "text",
683 "text": "Text",
684 "style": {
685 "italic": true
686 }
687 }
688 ]
689 }
690 ]
691 }))];
692 assert_eq!(
693 render_blocks_as_text(blocks, SlackReferences::default()),
694 "Text".to_string()
695 );
696 }
697
698 #[test]
699 fn test_with_strike_text() {
700 let blocks = vec![rich_text_block(serde_json::json!({
701 "type": "rich_text",
702 "elements": [
703 {
704 "type": "rich_text_section",
705 "elements": [
706 {
707 "type": "text",
708 "text": "Text",
709 "style": {
710 "strike": true
711 }
712 }
713 ]
714 }
715 ]
716 }))];
717 assert_eq!(
718 render_blocks_as_text(blocks, SlackReferences::default()),
719 "Text".to_string()
720 );
721 }
722
723 #[test]
724 fn test_with_code_text() {
725 let blocks = vec![rich_text_block(serde_json::json!({
726 "type": "rich_text",
727 "elements": [
728 {
729 "type": "rich_text_section",
730 "elements": [
731 {
732 "type": "text",
733 "text": "Text",
734 "style": {
735 "code": true
736 }
737 }
738 ]
739 }
740 ]
741 }))];
742 assert_eq!(
743 render_blocks_as_text(blocks, SlackReferences::default()),
744 "Text".to_string()
745 );
746 }
747
748 #[test]
749 fn test_with_styled_text() {
750 let blocks = vec![rich_text_block(serde_json::json!({
751 "type": "rich_text",
752 "elements": [
753 {
754 "type": "rich_text_section",
755 "elements": [
756 {
757 "type": "text",
758 "text": "Text",
759 "style": {
760 "bold": true,
761 "italic": true,
762 "strike": true
763 }
764 }
765 ]
766 }
767 ]
768 }))];
769 assert_eq!(
770 render_blocks_as_text(blocks, SlackReferences::default()),
771 "Text".to_string()
772 );
773 }
774 }
775
776 mod channel_element {
777 use super::*;
778
779 #[test]
780 fn test_with_channel_id() {
781 let blocks = vec![rich_text_block(serde_json::json!({
782 "type": "rich_text",
783 "elements": [
784 {
785 "type": "rich_text_section",
786 "elements": [
787 {
788 "type": "channel",
789 "channel_id": "C0123456"
790 }
791 ]
792 }
793 ]
794 }))];
795 assert_eq!(
796 render_blocks_as_text(blocks, SlackReferences::default()),
797 "#C0123456".to_string()
798 );
799 }
800
801 #[test]
802 fn test_with_channel_id_and_reference() {
803 let blocks = vec![rich_text_block(serde_json::json!({
804 "type": "rich_text",
805 "elements": [
806 {
807 "type": "rich_text_section",
808 "elements": [
809 {
810 "type": "channel",
811 "channel_id": "C0123456"
812 }
813 ]
814 }
815 ]
816 }))];
817 assert_eq!(
818 render_blocks_as_text(
819 blocks,
820 SlackReferences {
821 channels: HashMap::from([(
822 SlackChannelId("C0123456".to_string()),
823 Some("general".to_string())
824 )]),
825 ..SlackReferences::default()
826 }
827 ),
828 "#general".to_string()
829 );
830 }
831 }
832
833 mod user_element {
834 use super::*;
835
836 #[test]
837 fn test_with_user_id() {
838 let blocks = vec![rich_text_block(serde_json::json!({
839 "type": "rich_text",
840 "elements": [
841 {
842 "type": "rich_text_section",
843 "elements": [
844 {
845 "type": "user",
846 "user_id": "user1"
847 }
848 ]
849 }
850 ]
851 }))];
852 assert_eq!(
853 render_blocks_as_text(blocks, SlackReferences::default()),
854 "@user1".to_string()
855 );
856 }
857
858 #[test]
859 fn test_with_user_id_and_reference() {
860 let blocks = vec![rich_text_block(serde_json::json!({
861 "type": "rich_text",
862 "elements": [
863 {
864 "type": "rich_text_section",
865 "elements": [
866 {
867 "type": "user",
868 "user_id": "user1"
869 }
870 ]
871 }
872 ]
873 }))];
874 assert_eq!(
875 render_blocks_as_text(
876 blocks,
877 SlackReferences {
878 users: HashMap::from([(
879 SlackUserId("user1".to_string()),
880 Some("John Doe".to_string())
881 )]),
882 ..SlackReferences::default()
883 }
884 ),
885 "@John Doe".to_string()
886 );
887 }
888 }
889
890 mod usergroup_element {
891 use super::*;
892
893 #[test]
894 fn test_with_usergroup_id() {
895 let blocks = vec![rich_text_block(serde_json::json!({
896 "type": "rich_text",
897 "elements": [
898 {
899 "type": "rich_text_section",
900 "elements": [
901 {
902 "type": "usergroup",
903 "usergroup_id": "group1"
904 }
905 ]
906 }
907 ]
908 }))];
909 assert_eq!(
910 render_blocks_as_text(blocks, SlackReferences::default()),
911 "@group1".to_string()
912 );
913 }
914
915 #[test]
916 fn test_with_usergroup_id_and_reference() {
917 let blocks = vec![rich_text_block(serde_json::json!({
918 "type": "rich_text",
919 "elements": [
920 {
921 "type": "rich_text_section",
922 "elements": [
923 {
924 "type": "usergroup",
925 "usergroup_id": "group1"
926 }
927 ]
928 }
929 ]
930 }))];
931 assert_eq!(
932 render_blocks_as_text(
933 blocks,
934 SlackReferences {
935 usergroups: HashMap::from([(
936 SlackUserGroupId("group1".to_string()),
937 Some("Admins".to_string())
938 )]),
939 ..SlackReferences::default()
940 }
941 ),
942 "@Admins".to_string()
943 );
944 }
945 }
946
947 mod link_element {
948 use super::*;
949
950 #[test]
951 fn test_with_url() {
952 let blocks = vec![rich_text_block(serde_json::json!({
953 "type": "rich_text",
954 "elements": [
955 {
956 "type": "rich_text_section",
957 "elements": [
958 {
959 "type": "link",
960 "text": "example",
961 "url": "https://example.com"
962 }
963 ]
964 }
965 ]
966 }))];
967 assert_eq!(
968 render_blocks_as_text(blocks, SlackReferences::default()),
969 "example".to_string()
970 );
971 }
972 }
973
974 mod emoji_element {
975 use super::*;
976
977 #[test]
978 fn test_with_emoji() {
979 let blocks = vec![rich_text_block(serde_json::json!({
980 "type": "rich_text",
981 "elements": [
982 {
983 "type": "rich_text_section",
984 "elements": [
985 {
986 "type": "emoji",
987 "name": "wave"
988 }
989 ]
990 }
991 ]
992 }))];
993 assert_eq!(
994 render_blocks_as_text(blocks, SlackReferences::default()),
995 "👋".to_string()
996 );
997 }
998
999 #[test]
1000 fn test_with_emoji_with_skin_tone() {
1001 let blocks = vec![rich_text_block(serde_json::json!({
1002 "type": "rich_text",
1003 "elements": [
1004 {
1005 "type": "rich_text_section",
1006 "elements": [
1007 {
1008 "type": "emoji",
1009 "name": "wave::skin-tone-2"
1010 }
1011 ]
1012 }
1013 ]
1014 }))];
1015 assert_eq!(
1016 render_blocks_as_text(blocks, SlackReferences::default()),
1017 "👋🏻".to_string()
1018 );
1019 }
1020
1021 #[test]
1022 fn test_with_emoji_with_unknown_skin_tone() {
1023 let blocks = vec![rich_text_block(serde_json::json!({
1024 "type": "rich_text",
1025 "elements": [
1026 {
1027 "type": "rich_text_section",
1028 "elements": [
1029 {
1030 "type": "emoji",
1031 "name": "wave::skin-tone-42"
1032 }
1033 ]
1034 }
1035 ]
1036 }))];
1037 assert_eq!(
1038 render_blocks_as_text(blocks, SlackReferences::default()),
1039 "👋".to_string()
1040 );
1041 }
1042
1043 #[test]
1044 fn test_with_unknown_emoji() {
1045 let blocks = vec![rich_text_block(serde_json::json!({
1046 "type": "rich_text",
1047 "elements": [
1048 {
1049 "type": "rich_text_section",
1050 "elements": [
1051 {
1052 "type": "emoji",
1053 "name": "bbb"
1054 }
1055 ]
1056 }
1057 ]
1058 }))];
1059 assert_eq!(
1060 render_blocks_as_text(blocks, SlackReferences::default()),
1061 "".to_string()
1062 );
1063 }
1064 }
1065 }
1066
1067 mod rich_text_list {
1068 use super::*;
1069
1070 #[test]
1071 fn test_with_ordered_list() {
1072 let blocks = vec![rich_text_block(serde_json::json!({
1073 "type": "rich_text",
1074 "elements": [
1075 {
1076 "type": "rich_text_list",
1077 "style": "ordered",
1078 "elements": [
1079 {
1080 "type": "rich_text_section",
1081 "elements": [
1082 {
1083 "type": "text",
1084 "text": "Text1"
1085 }
1086 ]
1087 },
1088 {
1089 "type": "rich_text_section",
1090 "elements": [
1091 {
1092 "type": "text",
1093 "text": "Text2"
1094 }
1095 ]
1096 }
1097 ]
1098 },
1099 ]
1100 }))];
1101 assert_eq!(
1102 render_blocks_as_text(blocks, SlackReferences::default()),
1103 "1. Text1\n1. Text2".to_string()
1104 );
1105 }
1106
1107 #[test]
1108 fn test_with_bullet_list() {
1109 let blocks = vec![rich_text_block(serde_json::json!({
1110 "type": "rich_text",
1111 "elements": [
1112 {
1113 "type": "rich_text_list",
1114 "style": "bullet",
1115 "elements": [
1116 {
1117 "type": "rich_text_section",
1118 "elements": [
1119 {
1120 "type": "text",
1121 "text": "Text1"
1122 }
1123 ]
1124 },
1125 {
1126 "type": "rich_text_section",
1127 "elements": [
1128 {
1129 "type": "text",
1130 "text": "Text2"
1131 }
1132 ]
1133 }
1134 ]
1135 },
1136 ]
1137 }))];
1138 assert_eq!(
1139 render_blocks_as_text(blocks, SlackReferences::default()),
1140 "- Text1\n- Text2".to_string()
1141 );
1142 }
1143 }
1144
1145 mod rich_text_preformatted {
1146 use super::*;
1147
1148 #[test]
1149 fn test_with_text() {
1150 let blocks = vec![rich_text_block(serde_json::json!({
1151 "type": "rich_text",
1152 "elements": [
1153 {
1154 "type": "rich_text_preformatted",
1155 "elements": [
1156 {
1157 "type": "text",
1158 "text": "Text1"
1159 },
1160 {
1161 "type": "text",
1162 "text": "Text2"
1163 }
1164 ]
1165 },
1166 ]
1167 }))];
1168 assert_eq!(
1169 render_blocks_as_text(blocks, SlackReferences::default()),
1170 "Text1Text2".to_string()
1171 );
1172 }
1173 }
1174
1175 mod rich_text_quote {
1176 use super::*;
1177
1178 #[test]
1179 fn test_with_text() {
1180 let blocks = vec![rich_text_block(serde_json::json!({
1181 "type": "rich_text",
1182 "elements": [
1183 {
1184 "type": "rich_text_quote",
1185 "elements": [
1186 {
1187 "type": "text",
1188 "text": "Text1"
1189 },
1190 {
1191 "type": "text",
1192 "text": "Text2"
1193 }
1194 ]
1195 },
1196 ]
1197 }))];
1198 assert_eq!(
1199 render_blocks_as_text(blocks, SlackReferences::default()),
1200 "Text1Text2".to_string()
1201 );
1202 }
1203 }
1204 }
1205}