acdc_parser/options.rs
1pub use crate::safe_mode::SafeMode;
2
3use crate::{AttributeValue, DocumentAttributes};
4
5#[derive(Debug, Clone, Default)]
6#[non_exhaustive]
7pub struct Options {
8 pub safe_mode: SafeMode,
9 pub timings: bool,
10 pub document_attributes: DocumentAttributes,
11 /// Strict mode - fail on non-conformance instead of warn-and-continue.
12 ///
13 /// When enabled, issues that would normally result in a warning and fallback
14 /// behavior will instead cause parsing to fail. For example:
15 /// - Non-conforming manpage titles (not matching `name(volume)` format)
16 pub strict: bool,
17 /// Enable Setext-style (underlined) header parsing.
18 ///
19 /// When enabled, headers can use the legacy two-line syntax:
20 /// ```text
21 /// Document Title
22 /// ==============
23 /// ```
24 #[cfg(feature = "setext")]
25 pub setext: bool,
26}
27
28impl Options {
29 /// Create a new `OptionsBuilder` for fluent configuration.
30 ///
31 /// # Example
32 ///
33 /// ```
34 /// use acdc_parser::{Options, SafeMode};
35 ///
36 /// let options = Options::builder()
37 /// .with_safe_mode(SafeMode::Safe)
38 /// .with_timings()
39 /// .with_attribute("toc", "left")
40 /// .build();
41 /// ```
42 #[must_use]
43 pub fn builder() -> OptionsBuilder {
44 OptionsBuilder::default()
45 }
46
47 /// Create a new `Options` with default settings.
48 ///
49 /// Equivalent to `Options::default()`.
50 #[must_use]
51 pub fn new() -> Self {
52 Self::default()
53 }
54
55 /// Create a new `Options` with the given document attributes.
56 ///
57 /// # Example
58 ///
59 /// ```
60 /// use acdc_parser::{Options, DocumentAttributes, AttributeValue};
61 ///
62 /// let mut attrs = DocumentAttributes::default();
63 /// attrs.insert("toc".into(), AttributeValue::String("left".into()));
64 ///
65 /// let options = Options::with_attributes(attrs);
66 /// ```
67 #[must_use]
68 pub fn with_attributes(document_attributes: DocumentAttributes) -> Self {
69 Self {
70 document_attributes,
71 ..Default::default()
72 }
73 }
74}
75
76/// Builder for `Options` that provides an API for configuration.
77///
78/// Create an `OptionsBuilder` using `Options::builder()`.
79///
80/// # Example
81///
82/// ```
83/// use acdc_parser::{Options, SafeMode};
84///
85/// let options = Options::builder()
86/// .with_safe_mode(SafeMode::Safe)
87/// .with_timings()
88/// .with_attribute("toc", "left")
89/// .with_attribute("sectnums", true)
90/// .build();
91/// ```
92#[derive(Debug, Clone, Default)]
93#[non_exhaustive]
94pub struct OptionsBuilder {
95 safe_mode: SafeMode,
96 timings: bool,
97 document_attributes: DocumentAttributes,
98 strict: bool,
99 #[cfg(feature = "setext")]
100 setext: bool,
101}
102
103impl OptionsBuilder {
104 /// Set the safe mode for parsing.
105 ///
106 /// # Example
107 ///
108 /// ```
109 /// use acdc_parser::{Options, SafeMode};
110 ///
111 /// let options = Options::builder()
112 /// .with_safe_mode(SafeMode::Safe)
113 /// .build();
114 /// ```
115 #[must_use]
116 pub fn with_safe_mode(mut self, safe_mode: SafeMode) -> Self {
117 self.safe_mode = safe_mode;
118 self
119 }
120
121 /// Enable timing information during parsing.
122 ///
123 /// # Example
124 ///
125 /// ```
126 /// use acdc_parser::Options;
127 ///
128 /// let options = Options::builder()
129 /// .with_timings()
130 /// .build();
131 /// ```
132 #[must_use]
133 pub fn with_timings(mut self) -> Self {
134 self.timings = true;
135 self
136 }
137
138 /// Enable strict mode.
139 ///
140 /// When enabled, issues that would normally result in a warning and fallback
141 /// behavior will instead cause parsing to fail.
142 ///
143 /// # Example
144 ///
145 /// ```
146 /// use acdc_parser::Options;
147 ///
148 /// let options = Options::builder()
149 /// .with_strict()
150 /// .build();
151 /// ```
152 #[must_use]
153 pub fn with_strict(mut self) -> Self {
154 self.strict = true;
155 self
156 }
157
158 /// Add a document attribute with a string value.
159 ///
160 /// This is a convenience method that accepts various types for the value:
161 /// - `&str` becomes `AttributeValue::String`
162 /// - `bool` becomes `AttributeValue::Bool`
163 /// - `()` becomes `AttributeValue::None`
164 ///
165 /// # Example
166 ///
167 /// ```
168 /// use acdc_parser::Options;
169 ///
170 /// let options = Options::builder()
171 /// .with_attribute("toc", "left")
172 /// .with_attribute("sectnums", true)
173 /// .build();
174 /// ```
175 #[must_use]
176 pub fn with_attribute(
177 mut self,
178 name: impl Into<String>,
179 value: impl Into<AttributeValue>,
180 ) -> Self {
181 self.document_attributes.insert(name.into(), value.into());
182 self
183 }
184
185 /// Set all document attributes at once.
186 ///
187 /// # Example
188 ///
189 /// ```
190 /// use acdc_parser::{Options, DocumentAttributes, AttributeValue};
191 ///
192 /// let mut attrs = DocumentAttributes::default();
193 /// attrs.insert("toc".into(), AttributeValue::String("left".into()));
194 ///
195 /// let options = Options::builder()
196 /// .with_attributes(attrs)
197 /// .build();
198 /// ```
199 #[must_use]
200 pub fn with_attributes(mut self, document_attributes: DocumentAttributes) -> Self {
201 self.document_attributes = document_attributes;
202 self
203 }
204
205 /// Enable Setext-style (underlined) header parsing.
206 ///
207 /// When enabled, headers can use the legacy two-line syntax where
208 /// the title is underlined with `=`, `-`, `~`, `^`, or `+` characters.
209 ///
210 /// # Example
211 ///
212 /// ```ignore
213 /// use acdc_parser::Options;
214 ///
215 /// let options = Options::builder()
216 /// .with_setext()
217 /// .build();
218 /// ```
219 #[cfg(feature = "setext")]
220 #[must_use]
221 pub fn with_setext(mut self) -> Self {
222 self.setext = true;
223 self
224 }
225
226 /// Build the `Options` from this builder.
227 ///
228 /// # Example
229 ///
230 /// ```
231 /// use acdc_parser::{Options, SafeMode};
232 ///
233 /// let options = Options::builder()
234 /// .with_safe_mode(SafeMode::Safe)
235 /// .build();
236 /// ```
237 #[must_use]
238 pub fn build(self) -> Options {
239 Options {
240 safe_mode: self.safe_mode,
241 timings: self.timings,
242 document_attributes: self.document_attributes,
243 strict: self.strict,
244 #[cfg(feature = "setext")]
245 setext: self.setext,
246 }
247 }
248}