Skip to main content

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}