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