markdown_fmt/builder.rs
1use super::*;
2
3pub(crate) type CodeBlockFormatter = Box<dyn Fn(&str, String) -> String>;
4
5/// Builder for the [MarkdownFormatter](crate::MarkdownFormatter)
6pub struct FormatterBuilder {
7 code_block_formatter: CodeBlockFormatter,
8 config: Config,
9}
10
11impl FormatterBuilder {
12 /// Create a [FormatterBuilder] with custom [`Config`].
13 ///
14 /// ```rust
15 /// # use markdown_fmt::{Config, FormatterBuilder};
16 /// let builder = FormatterBuilder::with_config(Config {
17 /// max_width: Some(80),
18 /// ..Default::default()
19 /// });
20 /// let formatter = builder.build();
21 /// ```
22 pub fn with_config(config: Config) -> Self {
23 Self {
24 config,
25 ..Default::default()
26 }
27 }
28
29 /// Create a [FormatterBuilder] with a custom code block formatter.
30 ///
31 /// The closure used to reformat code blocks takes two arguments;
32 /// the [`info string`] and the complete code snippet
33 ///
34 /// ```rust
35 /// # use markdown_fmt::MarkdownFormatter;
36 /// # use markdown_fmt::FormatterBuilder;
37 /// let builder = FormatterBuilder::with_code_block_formatter(|info_string, code_block| {
38 /// // Set the code block formatting logic
39 /// match info_string.to_lowercase().as_str() {
40 /// "rust" => {
41 /// // format rust code
42 /// # code_block
43 /// }
44 /// _ => code_block,
45 /// }
46 /// });
47 /// let formatter = builder.build();
48 /// ```
49 /// [`info string`]: https://spec.commonmark.org/0.31.2/#fenced-code-blocks
50 pub fn with_code_block_formatter<F>(formatter: F) -> Self
51 where
52 F: Fn(&str, String) -> String + 'static,
53 {
54 let mut builder = Self::default();
55 builder.code_block_formatter(formatter);
56 builder
57 }
58
59 /// Build a [MarkdownFormatter](crate::MarkdownFormatter)
60 ///
61 /// ```rust
62 /// # use markdown_fmt::MarkdownFormatter;
63 /// # use markdown_fmt::FormatterBuilder;
64 /// let builder = FormatterBuilder::default();
65 /// let formatter: MarkdownFormatter = builder.build();
66 /// ```
67 pub fn build(self) -> crate::MarkdownFormatter {
68 crate::MarkdownFormatter::new(self.code_block_formatter, self.config)
69 }
70
71 /// Configure how code blocks should be reformatted after creating the [FormatterBuilder].
72 ///
73 /// The closure passed to `code_block_formatter` takes two arguments;
74 /// the [`info string`] and the complete code snippet
75 ///
76 /// [`info string`]: https://spec.commonmark.org/0.31.2/#fenced-code-blocks
77 pub fn code_block_formatter<F>(&mut self, formatter: F) -> &mut Self
78 where
79 F: Fn(&str, String) -> String + 'static,
80 {
81 self.code_block_formatter = Box::new(formatter);
82 self
83 }
84
85 /// Configure the max with when rewriting paragraphs.
86 ///
87 /// When set to [None], the deafault, paragraph width is left unchanged.
88 pub fn max_width(&mut self, max_width: Option<usize>) -> &mut Self {
89 self.config.max_width = max_width;
90 self
91 }
92
93 /// Set the configuration based on Steven Hé (Sīchàng)'s opinion.
94 pub fn sichanghe_config(&mut self) -> &mut Self {
95 self.config = Config::sichanghe_opinion();
96 self
97 }
98
99 /// Internal setter for Config. Used for testing
100 #[cfg(test)]
101 pub(crate) fn config(&mut self, config: Config) -> &mut Self {
102 self.config = config;
103 self
104 }
105}
106
107impl std::fmt::Debug for FormatterBuilder {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 f.debug_struct("FormatterBuilder")
110 .field("config", &self.config)
111 .finish()
112 }
113}
114
115/// Default formatter which leaves code blocks unformatted
116fn default_code_block_formatter(_info_str: &str, code_block: String) -> String {
117 code_block
118}
119
120impl Default for FormatterBuilder {
121 fn default() -> Self {
122 FormatterBuilder {
123 code_block_formatter: Box::new(default_code_block_formatter),
124 config: Config::default(),
125 }
126 }
127}