textwrap/options.rs
1//! Options for wrapping text.
2
3use crate::{LineEnding, WordSeparator, WordSplitter, WrapAlgorithm};
4
5/// Holds configuration options for wrapping and filling text.
6#[non_exhaustive]
7#[derive(Debug, Clone)]
8pub struct Options<'a> {
9 /// The width in columns at which the text will be wrapped.
10 pub width: usize,
11 /// Line ending used for breaking lines.
12 pub line_ending: LineEnding,
13 /// Indentation used for the first line of output. See the
14 /// [`Options::initial_indent`] method.
15 pub initial_indent: &'a str,
16 /// Indentation used for subsequent lines of output. See the
17 /// [`Options::subsequent_indent`] method.
18 pub subsequent_indent: &'a str,
19 /// Allow long words to be broken if they cannot fit on a line.
20 /// When set to `false`, some lines may be longer than
21 /// `self.width`. See the [`Options::break_words`] method.
22 pub break_words: bool,
23 /// Wrapping algorithm to use, see the [`WrapAlgorithm`] enum for
24 /// details.
25 pub wrap_algorithm: WrapAlgorithm,
26 /// The line breaking algorithm to use, see the [`WordSeparator`]
27 /// enum for an overview and possible implementations.
28 pub word_separator: WordSeparator,
29 /// The method for splitting words. This can be used to prohibit
30 /// splitting words on hyphens, or it can be used to implement
31 /// language-aware machine hyphenation.
32 pub word_splitter: WordSplitter,
33 /// Allow trailing spaces to be preserved at the end of the line.
34 pub preserve_trailing_space: bool,
35}
36
37impl<'a> From<&'a Options<'a>> for Options<'a> {
38 fn from(options: &'a Options<'a>) -> Self {
39 Self {
40 width: options.width,
41 line_ending: options.line_ending,
42 initial_indent: options.initial_indent,
43 subsequent_indent: options.subsequent_indent,
44 break_words: options.break_words,
45 word_separator: options.word_separator,
46 wrap_algorithm: options.wrap_algorithm,
47 word_splitter: options.word_splitter.clone(),
48 preserve_trailing_space: options.preserve_trailing_space,
49 }
50 }
51}
52
53impl From<usize> for Options<'_> {
54 fn from(width: usize) -> Self {
55 Options::new(width)
56 }
57}
58
59impl<'a> Options<'a> {
60 /// Creates a new [`Options`] with the specified width.
61 ///
62 /// The other fields are given default values as follows:
63 ///
64 /// ```
65 /// # use textwrap::{LineEnding, Options, WordSplitter, WordSeparator, WrapAlgorithm};
66 /// # let width = 80;
67 /// let options = Options::new(width);
68 /// assert_eq!(options.line_ending, LineEnding::LF);
69 /// assert_eq!(options.initial_indent, "");
70 /// assert_eq!(options.subsequent_indent, "");
71 /// assert_eq!(options.break_words, true);
72 /// assert_eq!(options.preserve_trailing_space, false);
73 ///
74 /// #[cfg(feature = "unicode-linebreak")]
75 /// assert_eq!(options.word_separator, WordSeparator::UnicodeBreakProperties);
76 /// #[cfg(not(feature = "unicode-linebreak"))]
77 /// assert_eq!(options.word_separator, WordSeparator::AsciiSpace);
78 ///
79 /// #[cfg(feature = "smawk")]
80 /// assert_eq!(options.wrap_algorithm, WrapAlgorithm::new_optimal_fit());
81 /// #[cfg(not(feature = "smawk"))]
82 /// assert_eq!(options.wrap_algorithm, WrapAlgorithm::FirstFit);
83 ///
84 /// assert_eq!(options.word_splitter, WordSplitter::HyphenSplitter);
85 /// ```
86 ///
87 /// Note that the default word separator and wrap algorithms
88 /// change based on the available Cargo features. The best
89 /// available algorithms are used by default.
90 pub const fn new(width: usize) -> Self {
91 Options {
92 width,
93 line_ending: LineEnding::LF,
94 initial_indent: "",
95 subsequent_indent: "",
96 break_words: true,
97 word_separator: WordSeparator::new(),
98 wrap_algorithm: WrapAlgorithm::new(),
99 word_splitter: WordSplitter::HyphenSplitter,
100 preserve_trailing_space: false,
101 }
102 }
103
104 /// Change [`self.line_ending`]. This specifies which of the
105 /// supported line endings should be used to break the lines of the
106 /// input text.
107 ///
108 /// # Examples
109 ///
110 /// ```
111 /// use textwrap::{refill, LineEnding, Options};
112 ///
113 /// let options = Options::new(15).line_ending(LineEnding::CRLF);
114 /// assert_eq!(refill("This is a little example.", options),
115 /// "This is a\r\nlittle example.");
116 /// ```
117 ///
118 /// [`self.line_ending`]: #structfield.line_ending
119 pub fn line_ending(self, line_ending: LineEnding) -> Self {
120 Options {
121 line_ending,
122 ..self
123 }
124 }
125
126 /// Set [`self.width`] to the given value.
127 ///
128 /// [`self.width`]: #structfield.width
129 pub fn width(self, width: usize) -> Self {
130 Options { width, ..self }
131 }
132
133 /// Change [`self.initial_indent`]. The initial indentation is
134 /// used on the very first line of output.
135 ///
136 /// # Examples
137 ///
138 /// Classic paragraph indentation can be achieved by specifying an
139 /// initial indentation and wrapping each paragraph by itself:
140 ///
141 /// ```
142 /// use textwrap::{wrap, Options};
143 ///
144 /// let options = Options::new(16).initial_indent(" ");
145 /// assert_eq!(wrap("This is a little example.", options),
146 /// vec![" This is a",
147 /// "little example."]);
148 /// ```
149 ///
150 /// [`self.initial_indent`]: #structfield.initial_indent
151 pub fn initial_indent(self, initial_indent: &'a str) -> Self {
152 Options {
153 initial_indent,
154 ..self
155 }
156 }
157
158 /// Change [`self.subsequent_indent`]. The subsequent indentation
159 /// is used on lines following the first line of output.
160 ///
161 /// # Examples
162 ///
163 /// Combining initial and subsequent indentation lets you format a
164 /// single paragraph as a bullet list:
165 ///
166 /// ```
167 /// use textwrap::{wrap, Options};
168 ///
169 /// let options = Options::new(12)
170 /// .initial_indent("* ")
171 /// .subsequent_indent(" ");
172 /// #[cfg(feature = "smawk")]
173 /// assert_eq!(wrap("This is a little example.", options),
174 /// vec!["* This is",
175 /// " a little",
176 /// " example."]);
177 ///
178 /// // Without the `smawk` feature, the wrapping is a little different:
179 /// #[cfg(not(feature = "smawk"))]
180 /// assert_eq!(wrap("This is a little example.", options),
181 /// vec!["* This is a",
182 /// " little",
183 /// " example."]);
184 /// ```
185 ///
186 /// [`self.subsequent_indent`]: #structfield.subsequent_indent
187 pub fn subsequent_indent(self, subsequent_indent: &'a str) -> Self {
188 Options {
189 subsequent_indent,
190 ..self
191 }
192 }
193
194 /// Change [`self.break_words`]. This controls if words longer
195 /// than `self.width` can be broken, or if they will be left
196 /// protruding into the right margin.
197 ///
198 /// See [`Options::word_splitter`] instead if you want to control
199 /// hyphenation.
200 ///
201 /// # Examples
202 ///
203 /// ```
204 /// use textwrap::{wrap, Options};
205 ///
206 /// let options = Options::new(4).break_words(true);
207 /// assert_eq!(wrap("This is a little example.", options),
208 /// vec!["This",
209 /// "is a",
210 /// "litt",
211 /// "le",
212 /// "exam",
213 /// "ple."]);
214 /// ```
215 ///
216 /// [`self.break_words`]: #structfield.break_words
217 pub fn break_words(self, break_words: bool) -> Self {
218 Options {
219 break_words,
220 ..self
221 }
222 }
223
224 /// Change [`self.word_separator`].
225 ///
226 /// See the [`WordSeparator`] enum for details on the choices.
227 ///
228 /// [`self.word_separator`]: #structfield.word_separator
229 pub fn word_separator(self, word_separator: WordSeparator) -> Options<'a> {
230 Options {
231 word_separator,
232 ..self
233 }
234 }
235
236 /// Change [`self.wrap_algorithm`].
237 ///
238 /// See the [`WrapAlgorithm`] enum for details on the choices.
239 ///
240 /// [`self.wrap_algorithm`]: #structfield.wrap_algorithm
241 pub fn wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a> {
242 Options {
243 wrap_algorithm,
244 ..self
245 }
246 }
247
248 /// Change [`self.word_splitter`]. The [`WordSplitter`] is used to
249 /// fit part of a word into the current line when wrapping text.
250 ///
251 /// See [`Options::break_words`] instead if you want to control the
252 /// handling of words longer than the line width.
253 ///
254 /// # Examples
255 ///
256 /// ```
257 /// use textwrap::{wrap, Options, WordSplitter};
258 ///
259 /// // The default is WordSplitter::HyphenSplitter.
260 /// let options = Options::new(5);
261 /// assert_eq!(wrap("foo-bar-baz", &options),
262 /// vec!["foo-", "bar-", "baz"]);
263 ///
264 /// // The word is now so long that break_words kicks in:
265 /// let options = Options::new(5)
266 /// .word_splitter(WordSplitter::NoHyphenation);
267 /// assert_eq!(wrap("foo-bar-baz", &options),
268 /// vec!["foo-b", "ar-ba", "z"]);
269 ///
270 /// // If you want no breaks at all, disable both:
271 /// let options = Options::new(5)
272 /// .break_words(false)
273 /// .word_splitter(WordSplitter::NoHyphenation);
274 /// assert_eq!(wrap("foo-bar-baz", &options),
275 /// vec!["foo-bar-baz"]);
276 /// ```
277 ///
278 /// [`self.word_splitter`]: #structfield.word_splitter
279 pub fn word_splitter(self, word_splitter: WordSplitter) -> Options<'a> {
280 Options {
281 word_splitter,
282 ..self
283 }
284 }
285
286 /// Change [`self.preserve_trailing_space`]. This controls if the
287 /// trailing spaces at the end of a line are preserved or trimmed.
288 ///
289 /// [`self.preserve_trailing_space`]: #structfield.preserve_trailing_space
290 pub fn preserve_trailing_space(self, preserve_trailing_space: bool) -> Options<'a> {
291 Options {
292 preserve_trailing_space,
293 ..self
294 }
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[test]
303 fn options_agree_with_usize() {
304 let opt_usize = Options::from(42_usize);
305 let opt_options = Options::new(42);
306
307 assert_eq!(opt_usize.width, opt_options.width);
308 assert_eq!(opt_usize.initial_indent, opt_options.initial_indent);
309 assert_eq!(opt_usize.subsequent_indent, opt_options.subsequent_indent);
310 assert_eq!(opt_usize.break_words, opt_options.break_words);
311 assert_eq!(
312 opt_usize.word_splitter.split_points("hello-world"),
313 opt_options.word_splitter.split_points("hello-world")
314 );
315 }
316}