acdc_parser/
options.rs

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