use std_::borrow::Borrow;
use std_::fmt;
use std_::str::CharIndices;
#[cfg(feature = "alloc")]
use alloc::string::String;
mod iterators;
pub use self::iterators::{CharIndicesFrom, KeyStr, RSplitWhile, SplitWhile};
pub trait StringExt: Borrow<str> {
fn previous_char_boundary(&self, mut index: usize) -> usize {
let this = self.borrow();
if index > this.len() {
return this.len();
}
index = index.saturating_sub(1);
while !this.is_char_boundary(index) {
index -= 1;
}
index
}
fn next_char_boundary(&self, mut index: usize) -> usize {
let this = self.borrow();
if index >= this.len() {
return this.len();
}
index += 1;
while !this.is_char_boundary(index) {
index += 1;
}
index
}
fn left_char_boundary(&self, mut index: usize) -> usize {
let this = self.borrow();
if index > this.len() {
return this.len();
}
while !this.is_char_boundary(index) {
index -= 1;
}
index
}
fn right_char_boundary(&self, mut index: usize) -> usize {
let this = self.borrow();
if index >= this.len() {
return this.len();
}
while !this.is_char_boundary(index) {
index += 1;
}
index
}
fn split_while<'a, P, T: Eq + Clone>(&'a self, mut mapper: P) -> SplitWhile<'a, P, T>
where
P: FnMut(char) -> T,
{
let this = self.borrow();
let mut c = this.chars();
SplitWhile {
last_left: mapper(c.next().unwrap_or(' ')),
last_right: mapper(c.next_back().unwrap_or(' ')),
mapper,
s: this,
}
}
fn rsplit_while<'a, P, T: Eq + Clone>(&'a self, mut mapper: P) -> RSplitWhile<'a, P, T>
where
P: FnMut(char) -> T,
{
let this = self.borrow();
let mut c = this.chars();
RSplitWhile {
last_left: mapper(c.next().unwrap_or(' ')),
last_right: mapper(c.next_back().unwrap_or(' ')),
mapper,
s: this,
}
}
fn get_nth_char_index(&self, nth: usize) -> Option<usize> {
self.borrow().char_indices().nth(nth).map(|(i, _)| i)
}
fn nth_char_index(&self, nth: usize) -> usize {
let this = self.borrow();
this.char_indices()
.nth(nth)
.map_or(this.len(), |(i, _)| i)
}
fn nth_char(&self, nth: usize) -> Option<char> {
self.borrow().chars().nth(nth)
}
fn first_chars(&self, n: usize) -> &str {
let this = self.borrow();
&this[..this.nth_char_index(n)]
}
fn last_chars(&self, n: usize) -> &str {
let this = self.borrow();
if n == 0 {
return &this[..0];
}
let index = this
.char_indices()
.rev()
.nth(n - 1)
.map_or(0, |(i, _)| i);
&this[index..]
}
#[allow(clippy::wrong_self_convention)]
fn from_nth_char(&self, n: usize) -> &str {
let this = self.borrow();
&this[this.nth_char_index(n)..]
}
fn calc_len_utf16(&self) -> usize {
self.borrow()
.chars()
.fold(0, |accum, c| accum + c.len_utf16())
}
fn get_char_at(&self, at_byte: usize) -> Option<char> {
let this = self.borrow();
if at_byte >= this.len() {
return None;
}
let start = this.left_char_boundary(at_byte);
this[start..].chars().nth(0)
}
fn char_indices_to(&self, to: usize) -> CharIndices<'_> {
let this = self.borrow();
let to = this.left_char_boundary(to);
this[..to].char_indices()
}
fn char_indices_from(&self, from: usize) -> CharIndicesFrom<'_> {
let this = self.borrow();
let from = this.left_char_boundary(from);
CharIndicesFrom {
offset: from,
iter: this[from..].char_indices(),
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
fn left_pad(&self, how_much: usize) -> String {
use alloc::string::ToString;
self.left_padder(how_much).to_string()
}
#[cfg_attr(not(feature = "alloc"), doc = " ```ignore")]
#[cfg_attr(feature = "alloc", doc = " ```rust")]
fn left_padder<'a>(&'a self, how_much: usize) -> LeftPadder<'a> {
LeftPadder::new(self.borrow(), how_much)
}
#[cfg(feature = "alloc")]
fn line_indentation(&self) -> usize {
let this = self.borrow().lines().next().unwrap_or("");
this.len() - this.trim_start().len()
}
#[cfg(feature = "alloc")]
fn min_indentation(&self) -> usize {
self.borrow()
.lines()
.filter(|l| !l.trim_start().is_empty())
.map(|v| v.line_indentation())
.min()
.unwrap_or(0)
}
#[cfg(feature = "alloc")]
fn max_indentation(&self) -> usize {
self.borrow()
.lines()
.filter(|l| !l.trim_start().is_empty())
.map(|v| v.line_indentation())
.max()
.unwrap_or(0)
}
}
impl<T: ?Sized> StringExt for T where T: Borrow<str> {}
#[derive(Clone, Copy, Debug)]
pub struct LeftPadder<'a> {
string: &'a str,
padding: usize,
}
impl<'a> LeftPadder<'a> {
pub fn new(string: &'a str, padding: usize) -> Self {
Self { string, padding }
}
}
impl<'a> fmt::Display for LeftPadder<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut first = true;
use std_::fmt::Write;
for line in self.string.lines() {
if !first {
f.write_char('\n')?;
}
const SPACES: &str = " ";
let has_non_whitespace = line.contains(|c: char| !c.is_whitespace());
let mut pad = if has_non_whitespace { self.padding } else { 0 };
while let Some(next) = pad.checked_sub(SPACES.len()) {
f.write_str(SPACES)?;
pad = next;
}
f.write_str(&SPACES[..pad])?;
fmt::Display::fmt(line, f)?;
first = false;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "alloc")]
fn test_left_pad() {
let s = "what\n the\n hall";
assert_eq!(s.left_pad(0), s);
assert_eq!(
"what\n the\n hall".left_pad(4),
" what\n the\n hall"
);
assert_eq!("\n\nfoo".left_pad(4), "\n\n foo");
}
#[test]
fn test_right_char_boundary() {
let word = "niño";
assert_eq!(word.right_char_boundary(0), 0);
assert_eq!(word.right_char_boundary(1), 1);
assert_eq!(word.right_char_boundary(2), 2);
assert_eq!(word.right_char_boundary(3), 4);
assert_eq!(word.right_char_boundary(4), 4);
assert_eq!(word.right_char_boundary(5), 5);
assert_eq!(word.right_char_boundary(6), 5);
assert_eq!(word.right_char_boundary(7), 5);
}
#[test]
#[cfg(feature = "alloc")]
fn test_char_indices_to() {
let word = "niño";
assert_eq!(
word.char_indices_to(0).map(|(_, c)| c).collect::<String>(),
""
);
assert_eq!(
word.char_indices_to(1).map(|(_, c)| c).collect::<String>(),
"n"
);
assert_eq!(
word.char_indices_to(2).map(|(_, c)| c).collect::<String>(),
"ni"
);
assert_eq!(
word.char_indices_to(3).map(|(_, c)| c).collect::<String>(),
"ni"
);
assert_eq!(
word.char_indices_to(4).map(|(_, c)| c).collect::<String>(),
"niñ"
);
assert_eq!(
word.char_indices_to(5).map(|(_, c)| c).collect::<String>(),
"niño"
);
assert_eq!(
word.char_indices_to(6).map(|(_, c)| c).collect::<String>(),
"niño"
);
assert_eq!(
word.char_indices_to(7).map(|(_, c)| c).collect::<String>(),
"niño"
);
}
}