Skip to content

Commit

Permalink
Allows the use of trait object WordSplitter in Wrapper.
Browse files Browse the repository at this point in the history
This commit presents a minimal solution for mgeisler#178 which is yet quite
flexible (as it allows using Box & Rc & others), as well as being fairly
minimal, mostly, it is just relaxing trait bounds, thus this is even
backwards compatible.

In summary, this committ just relaxes the trait bound on the type parameter of
`Wrapper`, to allow unsized `WordSpiltter`. While it is practically impossible to
construct a `Wrapper` with unsized `WordSpiltter`, once a sized one has been
created, and wrapped in some pointer type (such as Box), it can be coerced
into e.g. `Box<Wrapper<dyn WordSpiltter>>` replacing the type parameter by a
trait object.
  • Loading branch information
Cryptjar committed Oct 31, 2020
1 parent b224787 commit ee19fe0
Showing 1 changed file with 56 additions and 6 deletions.
62 changes: 56 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub use crate::splitting::{HyphenSplitter, NoHyphenation, WordSplitter};
/// line) so that the overall time and memory complexity is O(*n*) where
/// *n* is the length of the input string.
#[derive(Clone, Debug)]
pub struct Wrapper<'a, S: WordSplitter> {
pub struct Wrapper<'a, S: ?Sized + WordSplitter> {
/// The width in columns at which the text will be wrapped.
pub width: usize,
/// Indentation used for the first line of output.
Expand Down Expand Up @@ -257,6 +257,9 @@ impl<'a, S: WordSplitter> Wrapper<'a, S> {
..self
}
}
}

impl<'a, S: ?Sized + WordSplitter> Wrapper<'a, S> {

/// Fill a line of text at `self.width` characters.
///
Expand Down Expand Up @@ -381,6 +384,9 @@ impl<'a, S: WordSplitter> Wrapper<'a, S> {
}
}

}

impl<'a, S: WordSplitter> Wrapper<'a, S> {
/// Lazily wrap a line of text at `self.width` characters.
///
/// The [`WordSplitter`] stored in [`self.splitter`] is used
Expand Down Expand Up @@ -454,12 +460,12 @@ impl<'a, S: WordSplitter> Iterator for IntoWrapIter<'a, S> {
///
/// [`Wrapper::wrap_iter`]: struct.Wrapper.html#method.wrap_iter
#[derive(Debug)]
pub struct WrapIter<'w, 'a: 'w, S: WordSplitter> {
pub struct WrapIter<'w, 'a: 'w, S: ?Sized + WordSplitter> {
wrapper: &'w Wrapper<'a, S>,
inner: WrapIterImpl<'a>,
}

impl<'w, 'a: 'w, S: WordSplitter> Iterator for WrapIter<'w, 'a, S> {
impl<'w, 'a: 'w, S: ?Sized + WordSplitter> Iterator for WrapIter<'w, 'a, S> {
type Item = Cow<'a, str>;

fn next(&mut self) -> Option<Cow<'a, str>> {
Expand Down Expand Up @@ -497,7 +503,7 @@ struct WrapIterImpl<'a> {
}

impl<'a> WrapIterImpl<'a> {
fn new<S: WordSplitter>(wrapper: &Wrapper<'a, S>, s: &'a str) -> WrapIterImpl<'a> {
fn new<S: ?Sized + WordSplitter>(wrapper: &Wrapper<'a, S>, s: &'a str) -> WrapIterImpl<'a> {
WrapIterImpl {
source: s,
char_indices: s.char_indices(),
Expand All @@ -511,15 +517,15 @@ impl<'a> WrapIterImpl<'a> {
}
}

fn create_result_line<S: WordSplitter>(&self, wrapper: &Wrapper<'a, S>) -> Cow<'a, str> {
fn create_result_line<S: ?Sized + WordSplitter>(&self, wrapper: &Wrapper<'a, S>) -> Cow<'a, str> {
if self.start == 0 {
Cow::from(wrapper.initial_indent)
} else {
Cow::from(wrapper.subsequent_indent)
}
}

fn next<S: WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Option<Cow<'a, str>> {
fn next<S: ?Sized + WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Option<Cow<'a, str>> {
if self.finished {
return None;
}
Expand Down Expand Up @@ -1101,4 +1107,48 @@ mod tests {
String::from(green_hello) + "\n" + &blue_world
);
}

#[test]
fn boxing_outside() {
// Type annotations here are mostly commendatory (except for the dyn_ref)

let opt: Wrapper<NoHyphenation> = Wrapper::with_splitter(10, NoHyphenation);

let boxed: Box<Wrapper<NoHyphenation>> = Box::new(opt);

let mut dyn_box: Box<Wrapper<dyn WordSplitter>> = boxed;

// Using coercion to reference
let dyn_ref: &Wrapper<dyn WordSplitter> = &dyn_box;
assert_eq!(dyn_ref.wrap("foo bar-baz"), vec!["foo", "bar-baz"]);

// Replacing NoHyphenation with HyphenSplitter, without changing the type
dyn_box = Box::new(Wrapper::new(10));

// Using deref
assert_eq!(dyn_box.wrap("foo bar-baz"), vec!["foo bar-","baz"]);
}

#[test]
fn rcing_outside() {
use std::rc::Rc;

// Type annotations here are mostly commendatory (except for the dyn_ref)

let opt: Wrapper<NoHyphenation> = Wrapper::with_splitter(10, NoHyphenation);

let rced: Rc<Wrapper<NoHyphenation>> = Rc::new(opt);

let mut dyn_rc: Rc<Wrapper<dyn WordSplitter>> = rced;

// Using coercion to reference
let dyn_ref: &Wrapper<dyn WordSplitter> = &dyn_rc;
assert_eq!(dyn_ref.wrap("foo bar-baz"), vec!["foo", "bar-baz"]);

// Replacing NoHyphenation with HyphenSplitter, without changing the type
dyn_rc = Rc::new(Wrapper::new(10));

// Using deref
assert_eq!(dyn_rc.wrap("foo bar-baz"), vec!["foo bar-","baz"]);
}
}

0 comments on commit ee19fe0

Please sign in to comment.