1use crate::generate::{ToMarkdownString, ToMarkdownStringOption};
33use derive_more::{Display, From, Into, IntoIterator};
34
35#[derive(Debug, Eq, PartialEq, Hash, Default, Display)]
53#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
54pub struct MarkdownDocument {
55 content: MarkdownElementCollection,
56}
57
58impl MarkdownDocument {
59 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
61 Self {
62 content: content.into(),
63 }
64 }
65
66 pub fn content(&self) -> &MarkdownElementCollection {
68 &self.content
69 }
70}
71
72#[derive(Debug, Eq, PartialEq, Hash, Default, From, Into, IntoIterator, Display)]
74#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
75pub struct MarkdownElementCollection(Vec<MarkdownElement>);
76
77impl MarkdownElementCollection {
78 pub fn new(value: Vec<MarkdownElement>) -> Self {
80 Self(value)
81 }
82
83 pub fn get(&self) -> &Vec<MarkdownElement> {
85 &self.0
86 }
87}
88
89impl From<MarkdownElement> for MarkdownElementCollection {
90 fn from(value: MarkdownElement) -> Self {
91 MarkdownElementCollection::new(vec![value])
92 }
93}
94
95impl From<&str> for MarkdownElementCollection {
96 fn from(value: &str) -> Self {
97 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(value)))])
98 }
99}
100
101impl From<String> for MarkdownElementCollection {
102 fn from(value: String) -> Self {
103 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(value)))])
104 }
105}
106
107impl From<&String> for MarkdownElementCollection {
108 fn from(value: &String) -> Self {
109 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(value)))])
110 }
111}
112
113#[derive(Debug, Eq, PartialEq, Hash, Display)]
115pub enum MarkdownElement {
116 Plain(Box<Plain>),
118
119 ItalicsStar(Box<ItalicsStar>),
121
122 ItalicsUnderscore(Box<ItalicsUnderscore>),
124
125 Bold(Box<Bold>),
127
128 Underline(Box<Underline>),
130
131 Strikethrough(Box<Strikethrough>),
133
134 Spoiler(Box<Spoiler>),
136
137 OneLineCode(Box<OneLineCode>),
139
140 MultiLineCode(Box<MultiLineCode>),
142
143 BlockQuote(Box<BlockQuote>),
145}
146
147#[derive(Debug, Eq, PartialEq, Hash, Display)]
153#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
154pub struct Plain {
155 content: String,
156}
157
158impl Plain {
159 pub fn new(content: impl Into<String>) -> Self {
161 Self {
162 content: content.into(),
163 }
164 }
165
166 pub fn content(&self) -> &str {
168 &self.content
169 }
170}
171
172#[derive(Debug, Eq, PartialEq, Hash, Display)]
178#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
179pub struct ItalicsStar {
180 content: MarkdownElementCollection,
181}
182
183impl ItalicsStar {
184 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
186 Self {
187 content: content.into(),
188 }
189 }
190
191 pub fn content(&self) -> &MarkdownElementCollection {
193 &self.content
194 }
195}
196
197#[derive(Debug, Eq, PartialEq, Hash, Display)]
203#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
204pub struct ItalicsUnderscore {
205 content: MarkdownElementCollection,
206}
207
208impl ItalicsUnderscore {
209 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
211 Self {
212 content: content.into(),
213 }
214 }
215
216 pub fn content(&self) -> &MarkdownElementCollection {
218 &self.content
219 }
220}
221
222#[derive(Debug, Eq, PartialEq, Hash, Display)]
228#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
229pub struct Bold {
230 content: MarkdownElementCollection,
231}
232
233impl Bold {
234 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
236 Self {
237 content: content.into(),
238 }
239 }
240
241 pub fn content(&self) -> &MarkdownElementCollection {
243 &self.content
244 }
245}
246
247#[derive(Debug, Eq, PartialEq, Hash, Display)]
253#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
254pub struct Underline {
255 content: MarkdownElementCollection,
256}
257
258impl Underline {
259 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
261 Self {
262 content: content.into(),
263 }
264 }
265
266 pub fn content(&self) -> &MarkdownElementCollection {
268 &self.content
269 }
270}
271
272#[derive(Debug, Eq, PartialEq, Hash, Display)]
278#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
279pub struct Strikethrough {
280 content: MarkdownElementCollection,
281}
282
283impl Strikethrough {
284 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
286 Self {
287 content: content.into(),
288 }
289 }
290
291 pub fn content(&self) -> &MarkdownElementCollection {
293 &self.content
294 }
295}
296
297#[derive(Debug, Eq, PartialEq, Hash, Display)]
303#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
304pub struct Spoiler {
305 content: MarkdownElementCollection,
306}
307
308impl Spoiler {
309 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
311 Self {
312 content: content.into(),
313 }
314 }
315
316 pub fn content(&self) -> &MarkdownElementCollection {
318 &self.content
319 }
320}
321
322#[derive(Debug, Eq, PartialEq, Hash, Display)]
328#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
329pub struct OneLineCode {
330 content: String,
331}
332
333impl OneLineCode {
334 pub fn new(content: impl Into<String>) -> Self {
336 Self {
337 content: content.into(),
338 }
339 }
340
341 pub fn content(&self) -> &str {
343 &self.content
344 }
345}
346
347#[derive(Debug, Eq, PartialEq, Hash, Display)]
359#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
360pub struct MultiLineCode {
361 content: String,
362 language: Option<String>,
363}
364
365impl MultiLineCode {
366 pub fn new(content: impl Into<String>, language: Option<String>) -> Self {
368 Self {
372 content: content.into(),
373 language,
374 }
375 }
376
377 pub fn content(&self) -> &str {
379 &self.content
380 }
381
382 pub fn language(&self) -> Option<&str> {
384 self.language.as_deref()
385 }
386}
387
388#[derive(Debug, Eq, PartialEq, Hash, Display)]
397#[display(fmt = "{}", "self.to_markdown_string(&ToMarkdownStringOption::new())")]
398pub struct BlockQuote {
399 content: MarkdownElementCollection,
400}
401
402impl BlockQuote {
403 pub fn new(content: impl Into<MarkdownElementCollection>) -> Self {
405 Self {
406 content: content.into(),
407 }
408 }
409
410 pub fn content(&self) -> &MarkdownElementCollection {
412 &self.content
413 }
414}
415
416impl From<Plain> for MarkdownElement {
417 fn from(value: Plain) -> Self {
418 MarkdownElement::Plain(Box::new(value))
419 }
420}
421
422impl From<ItalicsStar> for MarkdownElement {
423 fn from(value: ItalicsStar) -> Self {
424 MarkdownElement::ItalicsStar(Box::new(value))
425 }
426}
427
428impl From<ItalicsUnderscore> for MarkdownElement {
429 fn from(value: ItalicsUnderscore) -> Self {
430 MarkdownElement::ItalicsUnderscore(Box::new(value))
431 }
432}
433
434impl From<Bold> for MarkdownElement {
435 fn from(value: Bold) -> Self {
436 MarkdownElement::Bold(Box::new(value))
437 }
438}
439
440impl From<Underline> for MarkdownElement {
441 fn from(value: Underline) -> Self {
442 MarkdownElement::Underline(Box::new(value))
443 }
444}
445
446impl From<Strikethrough> for MarkdownElement {
447 fn from(value: Strikethrough) -> Self {
448 MarkdownElement::Strikethrough(Box::new(value))
449 }
450}
451
452impl From<Spoiler> for MarkdownElement {
453 fn from(value: Spoiler) -> Self {
454 MarkdownElement::Spoiler(Box::new(value))
455 }
456}
457
458impl From<OneLineCode> for MarkdownElement {
459 fn from(value: OneLineCode) -> Self {
460 MarkdownElement::OneLineCode(Box::new(value))
461 }
462}
463
464impl From<MultiLineCode> for MarkdownElement {
465 fn from(value: MultiLineCode) -> Self {
466 MarkdownElement::MultiLineCode(Box::new(value))
467 }
468}
469
470impl From<BlockQuote> for MarkdownElement {
471 fn from(value: BlockQuote) -> Self {
472 MarkdownElement::BlockQuote(Box::new(value))
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479
480 fn example_text() -> MarkdownElementCollection {
481 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new("text")))])
482 }
483
484 #[test]
485 fn test_document_content() {
486 assert_eq!(
487 MarkdownDocument::new(example_text()).content(),
488 &example_text()
489 );
490 }
491
492 #[test]
493 fn test_element_collection_get() {
494 let test_case = || vec![MarkdownElement::Plain(Box::new(Plain::new("plain")))];
495 assert_eq!(
496 MarkdownElementCollection::new(test_case()).get(),
497 &test_case()
498 );
499 }
500
501 #[test]
502 fn test_element_collection_from_element() {
503 assert_eq!(
504 MarkdownElementCollection::from(MarkdownElement::Plain(Box::new(Plain::new(
505 "plain text"
506 )))),
507 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
508 "plain text"
509 )))]),
510 );
511 }
512
513 #[test]
514 fn test_element_collection_from_str() {
515 assert_eq!(
516 MarkdownElementCollection::from("plain text"),
517 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
518 "plain text"
519 )))]),
520 );
521 }
522
523 #[test]
524 fn test_element_collection_from_string() {
525 let test_case = "plain text".to_string();
526 assert_eq!(
527 MarkdownElementCollection::from(&test_case),
528 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
529 "plain text"
530 )))]),
531 );
532 assert_eq!(
533 MarkdownElementCollection::from(test_case),
534 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
535 "plain text"
536 )))]),
537 );
538 }
539
540 #[test]
541 fn test_plain_content() {
542 assert_eq!(Plain::new("plain text").content(), "plain text");
543 }
544
545 #[test]
546 fn test_italics_star_content() {
547 assert_eq!(ItalicsStar::new(example_text()).content(), &example_text());
548 }
549
550 #[test]
551 fn test_italics_underscore_content() {
552 assert_eq!(
553 ItalicsUnderscore::new(example_text()).content(),
554 &example_text()
555 );
556 }
557
558 #[test]
559 fn test_bold_content() {
560 assert_eq!(Bold::new(example_text()).content(), &example_text());
561 }
562
563 #[test]
564 fn test_underline_content() {
565 assert_eq!(Underline::new(example_text()).content(), &example_text());
566 }
567
568 #[test]
569 fn test_strikethrough_content() {
570 assert_eq!(
571 Strikethrough::new(example_text()).content(),
572 &example_text()
573 );
574 }
575
576 #[test]
577 fn test_spoiler_content() {
578 assert_eq!(Spoiler::new(example_text()).content(), &example_text());
579 }
580
581 #[test]
582 fn test_one_line_code_content() {
583 assert_eq!(OneLineCode::new("one line code").content(), "one line code");
584 }
585
586 #[test]
587 fn test_multi_line_code_content() {
588 assert_eq!(
589 MultiLineCode::new("multi\nline\ncode\n", None).content(),
590 "multi\nline\ncode\n"
591 );
592 }
593
594 #[test]
595 fn test_multi_line_code_language() {
596 assert_eq!(
597 MultiLineCode::new("multi\nline\ncode\n", Some("js".to_string())).language(),
598 Some("js")
599 );
600 assert_eq!(
601 MultiLineCode::new("multi\nline\ncode\n", None).language(),
602 None
603 );
604 }
605
606 #[test]
607 fn test_block_quote_content() {
608 let test_case = || {
609 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
610 "block quote\ntext",
611 )))])
612 };
613
614 assert_eq!(BlockQuote::new(test_case()).content(), &test_case());
615 }
616
617 #[test]
618 fn test_document_to_string() {
619 let ast = MarkdownDocument::new(MarkdownElementCollection::new(vec![
620 MarkdownElement::Bold(Box::new(Bold::new(MarkdownElementCollection::new(vec![
621 MarkdownElement::Plain(Box::new(Plain::new("bold"))),
622 ])))),
623 MarkdownElement::Plain(Box::new(Plain::new(" plain"))),
624 ]));
625
626 assert_eq!(ast.to_string(), "**bold** plain");
627 }
628
629 #[test]
630 fn test_element_collection_to_string() {
631 let ast = MarkdownElementCollection::new(vec![
632 MarkdownElement::Bold(Box::new(Bold::new(MarkdownElementCollection::new(vec![
633 MarkdownElement::Plain(Box::new(Plain::new("bold"))),
634 ])))),
635 MarkdownElement::Plain(Box::new(Plain::new(" plain "))),
636 MarkdownElement::Underline(Box::new(Underline::new(MarkdownElementCollection::new(
637 vec![MarkdownElement::Bold(Box::new(Bold::new(
638 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(
639 Plain::new("underline bold"),
640 ))]),
641 )))],
642 )))),
643 ]);
644
645 assert_eq!(ast.to_string(), "**bold** plain __**underline bold**__");
646 }
647
648 #[test]
649 fn test_plain_to_string() {
650 assert_eq!(Plain::new("plain text").to_string(), "plain text");
651 }
652
653 #[test]
654 fn test_italics_star_to_string() {
655 assert_eq!(ItalicsStar::new(example_text()).to_string(), "*text*");
656 }
657
658 #[test]
659 fn test_italics_underscore_to_string() {
660 assert_eq!(ItalicsUnderscore::new(example_text()).to_string(), "_text_");
661 }
662
663 #[test]
664 fn test_bold_to_string() {
665 assert_eq!(Bold::new(example_text()).to_string(), "**text**");
666 }
667
668 #[test]
669 fn test_underline_to_string() {
670 assert_eq!(Underline::new(example_text()).to_string(), "__text__");
671 }
672
673 #[test]
674 fn test_strikethrough_to_string() {
675 assert_eq!(Strikethrough::new(example_text()).to_string(), "~~text~~");
676 }
677
678 #[test]
679 fn test_spoiler_to_string() {
680 assert_eq!(Spoiler::new(example_text()).to_string(), "||text||");
681 }
682
683 #[test]
684 fn test_one_line_code_to_string() {
685 assert_eq!(
686 OneLineCode::new("one line code").to_string(),
687 "`one line code`"
688 );
689 }
690
691 #[test]
692 fn test_multi_line_code_to_string() {
693 assert_eq!(
694 MultiLineCode::new("\nmulti\nline\ncode\n", None).to_string(),
695 "```\nmulti\nline\ncode\n```"
696 );
697 assert_eq!(
698 MultiLineCode::new(" multi\nline\ncode\n", None).to_string(),
699 "``` multi\nline\ncode\n```"
700 );
701 assert_eq!(
702 MultiLineCode::new("multi line code", None).to_string(),
703 "```multi line code```"
704 );
705 assert_eq!(
706 MultiLineCode::new("\nmulti\nline\ncode\n", Some("js".to_string())).to_string(),
707 "```js\nmulti\nline\ncode\n```"
708 );
709 }
710
711 #[test]
712 fn test_block_quote_to_string() {
713 let test_case = || {
714 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
715 "block quote\ntext",
716 )))])
717 };
718
719 assert_eq!(
720 BlockQuote::new(test_case()).to_string(),
721 "> block quote\n> text"
722 );
723 }
724
725 #[test]
726 fn test_element_from_plain() {
727 assert_eq!(
728 MarkdownElement::from(Plain::new("plain text")),
729 MarkdownElement::Plain(Box::new(Plain::new("plain text")))
730 );
731 }
732
733 #[test]
734 fn test_element_from_italics_star() {
735 assert_eq!(
736 MarkdownElement::from(ItalicsStar::new(example_text())),
737 MarkdownElement::ItalicsStar(Box::new(ItalicsStar::new(example_text())))
738 );
739 }
740
741 #[test]
742 fn test_element_from_italics_underscore() {
743 assert_eq!(
744 MarkdownElement::from(ItalicsUnderscore::new(example_text())),
745 MarkdownElement::ItalicsUnderscore(Box::new(ItalicsUnderscore::new(example_text())))
746 );
747 }
748
749 #[test]
750 fn test_element_from_bold() {
751 assert_eq!(
752 MarkdownElement::from(Bold::new(example_text())),
753 MarkdownElement::Bold(Box::new(Bold::new(example_text())))
754 );
755 }
756
757 #[test]
758 fn test_element_from_underline() {
759 assert_eq!(
760 MarkdownElement::from(Underline::new(example_text())),
761 MarkdownElement::Underline(Box::new(Underline::new(example_text())))
762 );
763 }
764
765 #[test]
766 fn test_element_from_strikethrough() {
767 assert_eq!(
768 MarkdownElement::from(Strikethrough::new(example_text())),
769 MarkdownElement::Strikethrough(Box::new(Strikethrough::new(example_text())))
770 );
771 }
772
773 #[test]
774 fn test_element_from_spoiler() {
775 assert_eq!(
776 MarkdownElement::from(Spoiler::new(example_text())),
777 MarkdownElement::Spoiler(Box::new(Spoiler::new(example_text())))
778 );
779 }
780
781 #[test]
782 fn test_element_from_one_line_code() {
783 assert_eq!(
784 MarkdownElement::from(OneLineCode::new("one line code")),
785 MarkdownElement::OneLineCode(Box::new(OneLineCode::new("one line code")))
786 );
787 }
788
789 #[test]
790 fn test_element_from_multi_line_code() {
791 assert_eq!(
792 MarkdownElement::from(MultiLineCode::new(
793 "multi\nline\ncode\n",
794 Some("js".to_string())
795 )),
796 MarkdownElement::MultiLineCode(Box::new(MultiLineCode::new(
797 "multi\nline\ncode\n",
798 Some("js".to_string())
799 )))
800 );
801 }
802
803 #[test]
804 fn test_element_from_block_quote() {
805 let test_case = || {
806 MarkdownElementCollection::new(vec![MarkdownElement::Plain(Box::new(Plain::new(
807 "block quote\ntext",
808 )))])
809 };
810
811 assert_eq!(
812 MarkdownElement::from(BlockQuote::new(test_case())),
813 MarkdownElement::BlockQuote(Box::new(BlockQuote::new(test_case())))
814 );
815 }
816}