tbot/markup/
code_block.rs1use super::{html, markdown_v2, Nesting};
2use std::{
3 fmt::{self, Formatter, Write},
4 ops::Deref,
5};
6
7#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
11#[must_use = "formatters need to be formatted with `markdown_v2` or `html`"]
12pub struct CodeBlock<C, L = &'static str> {
13 code: C,
14 language: Option<L>,
15}
16
17impl<C, L: Deref<Target = str>> CodeBlock<C, L> {
18 pub fn language(mut self, language: L) -> Self {
24 if (&*language).contains('\n') {
25 panic!(
26 "[tbot] A code block's language may not contain line breaks: {}",
27 &*language,
28 );
29 }
30
31 if (&*language).contains('"') {
32 panic!(
33 "[tbot] A code block's language may not contain quotes: {}",
34 &*language,
35 );
36 }
37
38 self.language = Some(language);
39 self
40 }
41}
42
43pub fn code_block<I, T, L>(code: I) -> CodeBlock<I, L>
45where
46 for<'a> &'a I: IntoIterator<Item = &'a T>,
47 T: Deref<Target = str>,
48 L: Deref<Target = str>,
49{
50 CodeBlock {
51 code,
52 language: None,
53 }
54}
55
56impl<I, T, L> markdown_v2::Formattable for CodeBlock<I, L>
57where
58 for<'a> &'a I: IntoIterator<Item = &'a T>,
59 T: Deref<Target = str>,
60 L: Deref<Target = str>,
61{
62 fn format(&self, formatter: &mut Formatter, _: Nesting) -> fmt::Result {
63 formatter.write_str("```")?;
64 if let Some(language) = &self.language {
65 language
66 .deref()
67 .chars()
68 .map(|x| {
69 if markdown_v2::ESCAPED_CODE_CHARACTERS.contains(&x) {
70 formatter.write_char('\\')?;
71 }
72 formatter.write_char(x)
73 })
74 .collect::<Result<(), _>>()?;
75 }
76 formatter.write_char('\n')?;
77
78 (&self.code)
79 .into_iter()
80 .flat_map(|x| x.deref().chars())
81 .map(|x| {
82 if markdown_v2::ESCAPED_CODE_CHARACTERS.contains(&x) {
83 formatter.write_char('\\')?;
84 }
85 formatter.write_char(x)
86 })
87 .collect::<Result<(), _>>()?;
88 formatter.write_str("\n```")
89 }
90}
91
92impl<I, T, L> html::Formattable for CodeBlock<I, L>
93where
94 for<'a> &'a I: IntoIterator<Item = &'a T>,
95 T: Deref<Target = str>,
96 L: Deref<Target = str>,
97{
98 fn format(
99 &self,
100 formatter: &mut Formatter,
101 nesting: Nesting,
102 ) -> fmt::Result {
103 formatter.write_str("<pre>")?;
104
105 if let Some(language) = &self.language {
106 write!(formatter, "<code class=\"language-{}\">", &**language)?;
107 }
108
109 (&self.code)
110 .into_iter()
111 .map(|x| html::Formattable::format(&&**x, formatter, nesting))
112 .collect::<Result<(), _>>()?;
113
114 if self.language.is_some() {
115 formatter.write_str("</code>")?;
116 }
117
118 formatter.write_str("</pre>")
119 }
120}