Skip to content

Commit

Permalink
pull request #6
Browse files Browse the repository at this point in the history
misc: 🔧 polish various interfaces
  • Loading branch information
cratelyn authored Jul 18, 2024
2 parents cdb9a2d + ff021b0 commit ae84993
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 73 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "shear"
repository = "https://github.com/cratelyn/shear"
version = "0.2.0"
version = "0.3.0"

[features]
default = ["str"]
Expand Down
7 changes: 5 additions & 2 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
//!
//! this program prints a collection of strings, trimming them to fit a length (in bytes).

use {shear::str::LimitedExt, std::ops::Deref};
use {
shear::str::{ellipsis, Limited},
std::ops::Deref,
};

pub const WIDTH: usize = 50;

Expand All @@ -18,7 +21,7 @@ pub const FRUITS: &[&str] = &[

fn main() {
// helper fn: trim a string to `WIDTH`.
let trim = |s: &str| s.trim_to_length_ascii(WIDTH);
let trim = |s: &str| s.trim_to_length::<ellipsis::Ascii>(WIDTH);

// print each element, trimming it to a fixed length in bytes.
FRUITS.iter().map(Deref::deref).map(trim).for_each(|fruit| {
Expand Down
48 changes: 26 additions & 22 deletions src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@ use {

/// a trait for "limiting" an iterator.
///
/// this is used to wrap an iterator,
/// [`limited()`][Limited::limited] will transform an iterator, returning a [`LimitedIter<I>`] that
/// will be limited by `size`.
///
/// [`element_size()`][Limited::element_size] determines how "large" an item is. by default, an
/// identity function that counts items is used, always returning `1`. if an iterator's contents
/// are too long to fit in `size`, then [`contd()`][Limited::contd] will be yielded, indicating
/// that the iterator has been limited.
///
/// use [`str::Limited`][crate::str::Limited] to limit the contents of strings.
pub trait Limited: Iterator + Sized {
/// returns a "limited" iterator.
///
/// this will return at most `size` elements. once space is running out, the contents of
/// the iterator returned by [`Limited::contd()`] will be used to indicate that the value is
/// being "limited", or truncated.
///
/// e.g. for strings, represented as an iterator of characters, one might use `"..."`.
fn limited(self, length: usize) -> LimitedIter<Self> {
LimitedIter::new(self, length)
fn limited(self, size: usize) -> LimitedIter<Self> {
LimitedIter::new(self, size)
}

/// the type of iterator returned by [`Limited::contd()`].
type ContdIter: Iterator<Item = Self::Item>;
type Contd: IntoIterator<Item = Self::Item>;

/// returns an iterator of values to use as an indication of truncation.
fn contd() -> Self::ContdIter;
///
/// e.g. for strings, represented as an iterator of characters, one might use `"..."`.
fn contd() -> Self::Contd;

/// defines the size of an item in this iterator.
///
Expand All @@ -45,15 +49,15 @@ pub struct LimitedIter<I: Iterator> {

/// the inner finite state machine for a [`LimitedIter<I>`].
///
/// ```ignore
/// ┏━━━━━━━━━━┓
/// +----> ┃ limiting ┃ >--+
/// ┏━━━━━━━━━━┓ | ┗━━━━━━━━━━┛ | ┏━━━━━━━━━━┓
/// ┃ running ┃ -+----> >----------> >--+---> ┃ finished ┃
/// ┗━━━━━━━━━━┛ | ┏━━━━━━━━━━┓ | ┗━━━━━━━━━━┛
/// +----> ┃ tail ┃ >--+
/// ┗━━━━━━━━━━┛
/// ```text,ignore
/// ┏━━━━━━━━━━┓ ┏━━━━━━━━━━┓ ┏━━━━━━━━━━┓
/// ┃ running ┃ ---+--> ┃ tail ┃ -------> ┃ finished ┃
/// ┗━━━━━━━━━━┛ | ┗━━━━━━━━━━┛ | ┗━━━━━━━━━━┛
/// +---------------------+
/// ```
///
/// the iterator starts in the `Running` phase. it yields items until it eventually reaches the
/// terminal `Finished` state.
enum Inner<I: Iterator> {
/// the iterator is running.
Running {
Expand All @@ -78,8 +82,8 @@ enum Inner<I: Iterator> {

impl<I: Iterator + Limited> LimitedIter<I> {
/// returns a new [`LimitedIter`].
pub fn new(iter: I, length: usize) -> Self {
Inner::new(iter, length).pipe(|inner| Self { inner })
pub fn new(iter: I, size: usize) -> Self {
Inner::new(iter, size).pipe(|inner| Self { inner })
}
}

Expand Down Expand Up @@ -187,7 +191,7 @@ impl<I: Iterator + Limited> Inner<I> {
/// returns a new [`Inner`].
fn new(iter: I, total: usize) -> Self {
// collect the continuation sequence, and find out how large it is.
let contd = I::contd().collect::<Vec<_>>();
let contd = I::contd().into_iter().collect::<Vec<_>>();
let contd_size = contd.iter().map(I::element_size).sum();

match total.checked_sub(contd_size) {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@

/// [`Iterator`] limiting.
///
/// see [`Limited::limited()`][self::iter::Limited::limited] for more information.
/// see [`Limited`][self::iter::Limited] for more information.
pub mod iter;

/// [`String`] limiting.
///
/// see [`Limited::trim_to_length()`][self::str::Limited::trim_to_length] for more information.
/// see [`Limited`][self::str::Limited] for more information.
#[cfg(feature = "str")]
pub mod str;
29 changes: 0 additions & 29 deletions src/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,35 +74,6 @@ pub trait Limited {
fn trim_to_width<E: Ellipsis>(&self, length: usize) -> String;
}

/// an extension trait of [`Limited<E>`].
///
/// this defines methods that supply the [`Ellipsis`] to use when limiting a string e.g.,
/// an [`ellipsis::Ascii`] ellipsis `"..."`.
pub trait LimitedExt: Limited + AsRef<str> + Sized {
/// trim the string using an ascii `"..."` ellipsis.
///
/// # examples
///
/// ```
/// use shear::str::LimitedExt;
///
/// let s = "a very long string value";
/// let limited = s.trim_to_length_ascii(18);
///
/// assert_eq!(limited, "a very long str...");
/// assert_eq!(limited.len(), 18);
/// ```
fn trim_to_length_ascii(&self, length: usize) -> String {
<Self as Limited>::trim_to_length::<ellipsis::Ascii>(self, length)
}

/// trim the string using a horizontal `"…"` ellipsis.
fn trim_to_length_utf8_horizontal(&self, length: usize) -> String {
<Self as Limited>::trim_to_length::<ellipsis::Horizontal>(self, length)
}
}
impl<S> LimitedExt for S where S: Limited + AsRef<str> {}

// === impl s: asref<str> ===

impl<S> Limited for S
Expand Down
8 changes: 4 additions & 4 deletions src/str/trim_to_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ where
I: Iterator<Item = char> + Sized,
E: Ellipsis,
{
fn limited(self, length: usize) -> LimitedIter<Self> {
LimitedIter::new(self, length)
fn limited(self, size: usize) -> LimitedIter<Self> {
LimitedIter::new(self, size)
}

type ContdIter = std::str::Chars<'static>;
type Contd = std::str::Chars<'static>;

fn contd() -> Self::ContdIter {
fn contd() -> Self::Contd {
E::ellipsis().chars()
}

Expand Down
8 changes: 4 additions & 4 deletions src/str/trim_to_width.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ where
I: Iterator<Item = char> + Sized,
E: Ellipsis,
{
fn limited(self, length: usize) -> LimitedIter<Self> {
LimitedIter::new(self, length)
fn limited(self, size: usize) -> LimitedIter<Self> {
LimitedIter::new(self, size)
}

type ContdIter = std::str::Chars<'static>;
type Contd = std::str::Chars<'static>;

fn contd() -> Self::ContdIter {
fn contd() -> Self::Contd {
E::ellipsis().chars()
}

Expand Down
14 changes: 9 additions & 5 deletions tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
#![cfg(feature = "str")]

use {
self::strategy::TestInput, proptest::proptest, regex::Regex, shear::str::LimitedExt, tap::Pipe,
self::strategy::TestInput,
proptest::proptest,
regex::Regex,
shear::str::{ellipsis, Limited},
tap::Pipe,
};

mod strategy;
Expand All @@ -18,9 +22,9 @@ mod relevant_types_can_be_limited {
use super::*;

#[allow(unreachable_code, unused_variables, clippy::diverging_sub_expression)]
fn can_be_limited<T: shear::str::LimitedExt>() {
fn can_be_limited<T: shear::str::Limited>() {
let value: T = unimplemented!("");
value.trim_to_length_ascii(0).pipe(drop);
value.trim_to_length::<ellipsis::Ascii>(0).pipe(drop);
}

#[allow(dead_code)] // this is a compile-time check that needn't be called.
Expand All @@ -44,7 +48,7 @@ mod empty_str_can_be_limited {
}

fn empty_str_can_be_limited_(len: usize) {
"".trim_to_length_ascii(len)
"".trim_to_length::<ellipsis::Ascii>(len)
.pipe(|s| assert_eq!(s, "", "limited string should still be empty"))
}
}
Expand Down Expand Up @@ -152,7 +156,7 @@ mod strs_can_be_truncated {
}

fn strs_can_be_truncated_(TestInput { value, length }: TestInput) {
let limited = value.clone().trim_to_length_ascii(length);
let limited = value.clone().trim_to_length::<ellipsis::Ascii>(length);
let expected: Regex = {
let prefix = {
let mut s = String::new();
Expand Down
6 changes: 3 additions & 3 deletions tests/test_char_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ impl<'a> From<Chars<'a>> for TestIter<'a> {
}

impl<'a> Limited for TestIter<'a> {
type ContdIter = std::array::IntoIter<char, 3>;
type Contd = std::str::Chars<'static>;

fn contd() -> Self::ContdIter {
['.', '.', '.'].into_iter()
fn contd() -> Self::Contd {
"...".chars()
}

fn element_size(_: &Self::Item) -> usize {
Expand Down

0 comments on commit ae84993

Please sign in to comment.