namumark_parser/span/semantic_span/
mod.rs1use crate::{span_list, Result, Span};
2use nom::{
3 branch::alt,
4 bytes::complete::{tag, take_until},
5};
6
7#[derive(Debug, PartialEq)]
8pub enum SemanticSpan {
9 Strong(Vec<Span>),
10 Emphasis(Vec<Span>),
11 Delete(Vec<Span>),
12 Underline(Vec<Span>),
13 Superscript(Vec<Span>),
14 Subscript(Vec<Span>),
15}
16
17pub(crate) fn semantic_span(input: &str) -> Result<SemanticSpan> {
18 alt((
19 strong,
20 emphasis,
21 delete1,
22 delete2,
23 underline,
24 superscript,
25 subscript,
26 ))(input)
27}
28
29pub(crate) fn starts_with_sematic_span(input: &str) -> bool {
30 expect_strong(input).is_ok()
31 || expect_emphasis(input).is_ok()
32 || expect_delete1(input).is_ok()
33 || expect_delete2(input).is_ok()
34 || expect_underline(input).is_ok()
35 || expect_superscript(input).is_ok()
36 || expect_subscript(input).is_ok()
37}
38
39macro_rules! semantic_span {
40 ($name:ident, $expect_with_name:ident, $marker:expr, $variant:ident) => {
41 fn $name(input: &str) -> Result<SemanticSpan> {
42 let (input, span_input) = $expect_with_name(input)?;
43 let span_list = span_list(span_input);
44
45 Ok((input, SemanticSpan::$variant(span_list)))
46 }
47
48 fn $expect_with_name(input: &str) -> Result<&str> {
49 const MARKER: &'static str = $marker;
50 const MARKER_COUNT: usize = MARKER.len();
51
52 fn start(input: &str) -> Result {
53 let (input, _) = tag(MARKER)(input)?;
54
55 Ok((input, ()))
56 };
57
58 fn end(input: &str) -> Result<&str> {
59 let (input, span_input) = take_until(MARKER)(input)?;
60
61 Ok((&input[MARKER_COUNT..], span_input))
62 };
63
64 let (input, _) = start(input)?;
65 let (input, span_input) = end(input)?;
66
67 Ok((input, span_input))
68 }
69 };
70}
71
72semantic_span!(strong, expect_strong, "'''", Strong);
73semantic_span!(emphasis, expect_emphasis, "''", Emphasis);
74semantic_span!(delete1, expect_delete1, "~~", Delete);
75semantic_span!(delete2, expect_delete2, "--", Delete);
76semantic_span!(underline, expect_underline, "__", Underline);
77semantic_span!(superscript, expect_superscript, "^^", Superscript);
78semantic_span!(subscript, expect_subscript, ",,", Subscript);
79
80#[cfg(test)]
81mod delete_tests {
82 use crate::*;
83
84 #[test]
85 fn basic1() {
86 let source = "~~Danuel~~";
87 assert_eq!(
88 span_list(source),
89 vec![Span::Semantic(SemanticSpan::Delete(vec![Span::Inline(
90 "Danuel".to_owned()
91 )]))]
92 );
93 }
94
95 #[test]
96 fn basic2() {
97 let source = "--Danuel--";
98 assert_eq!(
99 span_list(source),
100 vec![Span::Semantic(SemanticSpan::Delete(vec![Span::Inline(
101 "Danuel".to_owned()
102 )]))]
103 );
104 }
105}
106
107#[cfg(test)]
108mod emphasis_tests {
109 use crate::*;
110
111 #[test]
112 fn basic() {
113 let source = "''Danuel''";
114 assert_eq!(
115 span_list(source),
116 vec![Span::Semantic(SemanticSpan::Emphasis(vec![Span::Inline(
117 "Danuel".to_owned()
118 )]))]
119 );
120 }
121}
122
123#[cfg(test)]
124mod strong_tests {
125 use crate::*;
126
127 #[test]
128 fn basic() {
129 let source = "'''Danuel'''";
130 assert_eq!(
131 span_list(source),
132 vec![Span::Semantic(SemanticSpan::Strong(vec![Span::Inline(
133 "Danuel".to_owned()
134 )]))]
135 );
136 }
137}
138
139#[cfg(test)]
140mod subscript_tests {
141 use crate::*;
142
143 #[test]
144 fn basic() {
145 let source = ",,Danuel,,";
146 assert_eq!(
147 span_list(source),
148 vec![Span::Semantic(SemanticSpan::Subscript(vec![Span::Inline(
149 "Danuel".to_owned()
150 )]))]
151 );
152 }
153}
154
155#[cfg(test)]
156mod superscript_tests {
157 use crate::*;
158
159 #[test]
160 fn basic() {
161 let source = "^^Danuel^^";
162 assert_eq!(
163 span_list(source),
164 vec![Span::Semantic(SemanticSpan::Superscript(vec![
165 Span::Inline("Danuel".to_owned())
166 ]))]
167 );
168 }
169}
170
171#[cfg(test)]
172mod underline_tests {
173 use crate::*;
174
175 #[test]
176 fn basic() {
177 let source = "__Danuel__";
178 assert_eq!(
179 span_list(source),
180 vec![Span::Semantic(SemanticSpan::Underline(vec![Span::Inline(
181 "Danuel".to_owned()
182 )]))]
183 );
184 }
185}