base_markdown/
code_fence.rs1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct CodeFence {
12 pub character: char,
14 pub length: usize,
16 pub indent: usize,
19}
20
21impl CodeFence {
22 pub(crate) fn frontmatter_open(bytes: &[u8]) -> Option<Self> {
23 let marker = *bytes.first()?;
24 if !matches!(marker, b'-' | b'+' | b'`' | b'~') {
25 return None;
26 }
27 let length = bytes.iter().take_while(|&&byte| byte == marker).count();
28 if length < 3 || (matches!(marker, b'-' | b'+') && length != 3) {
29 return None;
30 }
31 Some(Self {
32 character: char::from(marker),
33 length,
34 indent: 0,
35 })
36 }
37
38 pub(crate) fn valid_frontmatter_info(self, info: &[u8]) -> bool {
39 !info.iter().any(|byte| matches!(byte, b'\r' | b'\n'))
40 && (!matches!(self.character, '`' | '~')
41 || !info.iter().all(|byte| matches!(byte, b' ' | b'\t')))
42 && (self.character != '`' || !info.contains(&b'`'))
43 }
44
45 pub(crate) fn frontmatter_close(self, line: &[u8]) -> Option<usize> {
47 let count = line
48 .iter()
49 .take_while(|&&byte| char::from(byte) == self.character)
50 .count();
51 (count >= self.length
52 && (!matches!(self.character, '-' | '+') || count == self.length)
53 && line[count..]
54 .iter()
55 .all(|byte| matches!(byte, b' ' | b'\t')))
56 .then_some(count)
57 }
58}