Skip to main content

namumark_parser/span/semantic_span/
mod.rs

1use 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}