(code: &str)
| 103 | out.to_string() |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | #[derive(Default)] |
| 108 | pub struct AnchorIds { |
| 109 | next_suffix: HashMap<String, usize>, |
| 110 | emitted: HashSet<String>, |
| 111 | } |
| 112 | |
| 113 | pub fn unique_anchor_id(text: &str, seen: &mut AnchorIds) -> String { |
| 114 | let base = anchor_id(text); |
| 115 | let suffix = seen.next_suffix.entry(base.clone()).or_insert(0); |
| 116 | loop { |
| 117 | let id = if *suffix == 0 { |
| 118 | base.clone() |
| 119 | } else { |
| 120 | format!("{base}-{suffix}") |
| 121 | }; |
| 122 | *suffix += 1; |
| 123 | if seen.emitted.insert(id.clone()) { |
| 124 | return id; |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | fn heading_level_num(level: pulldown_cmark::HeadingLevel) -> u8 { |
| 130 | match level { |
| 131 | pulldown_cmark::HeadingLevel::H1 => 1, |
| 132 | pulldown_cmark::HeadingLevel::H2 => 2, |
| 133 | pulldown_cmark::HeadingLevel::H3 => 3, |
| 134 | pulldown_cmark::HeadingLevel::H4 => 4, |
| 135 | pulldown_cmark::HeadingLevel::H5 => 5, |
| 136 | pulldown_cmark::HeadingLevel::H6 => 6, |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | struct CodeBlock { |
| 141 | lang: String, |
| 142 | code: String, |
| 143 | } |
| 144 | |
| 145 | impl CodeBlock { |
| 146 | fn new(kind: CodeBlockKind<'_>) -> Self { |
| 147 | let lang = match kind { |
| 148 | CodeBlockKind::Fenced(info) => info.split_whitespace().next().unwrap_or("").to_string(), |
| 149 | CodeBlockKind::Indented => String::new(), |
| 150 | }; |
| 151 | Self { |
| 152 | lang, |
| 153 | code: String::new(), |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | fn render(&self) -> String { |
| 158 | code_block_html(&self.lang, &self.code) |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | pub fn code_block_html(lang: &str, code: &str) -> String { |
no test coverage detected