diff --git a/src/lib.rs b/src/lib.rs index a674725f..f3179db2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,7 +137,7 @@ pub trait WrapOptions { /// Holds settings for wrapping and filling text. #[derive(Debug)] -pub struct Options<'a> { +pub struct Options<'a, S = Box> { /// The width in columns at which the text will be wrapped. pub width: usize, /// Indentation used for the first line of output. @@ -151,7 +151,7 @@ pub struct Options<'a> { /// The method for splitting words. If the `hyphenation` feature /// is enabled, you can use a `hyphenation::Standard` dictionary /// here to get language-aware hyphenation. - pub splitter: Box, + pub splitter: S, } /// Allows using an `Options` with [`wrap`] and [`fill`]: @@ -169,7 +169,7 @@ pub struct Options<'a> { /// /// [`wrap`]: fn.wrap.html /// [`fill`]: fn.fill.html -impl WrapOptions for &Options<'_> { +impl WrapOptions for &Options<'_, S> { #[inline] fn width(&self) -> usize { self.width @@ -230,15 +230,51 @@ impl WrapOptions for usize { } } -impl<'a> Options<'a> { - /// Creates a new `Options` with the specified width. Equivalent +/// Const constructors for a good old HyphenSplitter Options, specifically. +impl<'a> Options<'a, HyphenSplitter> { + /// Creates a new `Options` with the specified width and a static dispatch `HyphenSplitter`. Equivalent /// to /// /// ``` /// # use textwrap::{Options, HyphenSplitter}; + /// # const width: usize = 80; + /// # const actual: Options<'static, HyphenSplitter> = Options::new_const(width); + /// # let expected = + /// Options { + /// width: width, + /// initial_indent: "", + /// subsequent_indent: "", + /// break_words: true, + /// splitter: HyphenSplitter, + /// } + /// # ; + /// # assert_eq!(actual.width, expected.width); + /// # assert_eq!(actual.initial_indent, expected.initial_indent); + /// # assert_eq!(actual.subsequent_indent, expected.subsequent_indent); + /// # assert_eq!(actual.break_words, expected.break_words); + /// # let expected_coerced: Options<'static, HyphenSplitter> = expected; + /// ``` + pub const fn new_const(width: usize) -> Options<'static, HyphenSplitter> { + Options { + width: width, + initial_indent: "", + subsequent_indent: "", + break_words: true, + splitter: HyphenSplitter, + } + } +} + +/// Constructors for boxed Options, specifically. +impl<'a> Options<'a> { + /// Creates a new `Options` with the specified width and dynamic dispatch. Equivalent + /// to + /// + /// ``` + /// # use textwrap::{Options, HyphenSplitter, WordSplitter}; /// # let width = 80; /// # let actual = Options::new(width); - /// # let expected = + /// # let expected: Options = /// Options { /// width: width, /// initial_indent: "", @@ -251,8 +287,9 @@ impl<'a> Options<'a> { /// # assert_eq!(actual.initial_indent, expected.initial_indent); /// # assert_eq!(actual.subsequent_indent, expected.subsequent_indent); /// # assert_eq!(actual.break_words, expected.break_words); + /// # let expected_coerced: Options<'static, Box> = expected; /// ``` - pub fn new(width: usize) -> Options<'static> { + pub fn new(width: usize) -> Self { Options { width: width, initial_indent: "", @@ -280,9 +317,47 @@ impl<'a> Options<'a> { /// **Note:** Only available when the `terminal_size` feature is /// enabled. #[cfg(feature = "terminal_size")] - pub fn with_termwidth() -> Options<'static> { - Options::new(termwidth()) + pub fn with_termwidth() -> Self { + Self::new(termwidth()) } +} +impl<'a, S> Options<'a, S> { + + /// Creates a new `Options` with the specified width and splitter. Equivalent + /// to + /// + /// ``` + /// # use textwrap::{Options, NoHyphenation}; + /// # const splitter: NoHyphenation = NoHyphenation; + /// # const width: usize = 80; + /// # const actual: Options<'static, NoHyphenation> = Options::with_splitter(width, splitter); + /// # let expected = + /// Options { + /// width: width, + /// initial_indent: "", + /// subsequent_indent: "", + /// break_words: true, + /// splitter: splitter, + /// } + /// # ; + /// # assert_eq!(actual.width, expected.width); + /// # assert_eq!(actual.initial_indent, expected.initial_indent); + /// # assert_eq!(actual.subsequent_indent, expected.subsequent_indent); + /// # assert_eq!(actual.break_words, expected.break_words); + /// # let expected_coerced: Options<'static, NoHyphenation> = expected; + /// ``` + pub const fn with_splitter(width: usize, splitter: S) -> Self { + Options { + width, + initial_indent: "", + subsequent_indent: "", + break_words: true, + splitter, + } + } +} + +impl<'a, S: WordSplitter> Options<'a, S> { /// Change [`self.initial_indent`]. The initial indentation is /// used on the very first line of output. @@ -300,7 +375,7 @@ impl<'a> Options<'a> { /// ``` /// /// [`self.initial_indent`]: #structfield.initial_indent - pub fn initial_indent(self, indent: &'a str) -> Options<'a> { + pub fn initial_indent(self, indent: &'a str) -> Self { Options { initial_indent: indent, ..self @@ -325,7 +400,7 @@ impl<'a> Options<'a> { /// ``` /// /// [`self.subsequent_indent`]: #structfield.subsequent_indent - pub fn subsequent_indent(self, indent: &'a str) -> Options<'a> { + pub fn subsequent_indent(self, indent: &'a str) -> Self { Options { subsequent_indent: indent, ..self @@ -337,7 +412,7 @@ impl<'a> Options<'a> { /// sticking out into the right margin. /// /// [`self.break_words`]: #structfield.break_words - pub fn break_words(self, setting: bool) -> Options<'a> { + pub fn break_words(self, setting: bool) -> Self { Options { break_words: setting, ..self @@ -349,7 +424,7 @@ impl<'a> Options<'a> { /// /// [`self.splitter`]: #structfield.splitter /// [`WordSplitter`]: trait.WordSplitter.html - pub fn splitter(self, splitter: Box) -> Options<'a> { + pub fn splitter(self, splitter: S) -> Self { Options { splitter: splitter, ..self diff --git a/src/splitting.rs b/src/splitting.rs index c35ae584..bae497b2 100644 --- a/src/splitting.rs +++ b/src/splitting.rs @@ -41,6 +41,21 @@ pub trait WordSplitter: std::fmt::Debug { fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)>; } +impl WordSplitter for Box { + fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)> { + use std::ops::Deref; + self.deref().split(word) + } +} +/* Alternative, also adds impls for specific Box i.e. Box +impl WordSplitter for Box { + fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)> { + use std::ops::Deref; + self.deref().split(word) + } +} +*/ + /// Use this as a [`Options.splitter`] to avoid any kind of /// hyphenation: ///