pub trait StringPad {
#[must_use]
fn left_pad(&self, size: usize) -> String;
#[must_use]
fn left_pad_with(&self, size: usize, pad_char: char) -> String;
#[must_use]
fn left_pad_with_str(&self, size: usize, pad_str: &str) -> String;
#[must_use]
fn right_pad(&self, size: usize) -> String;
#[must_use]
fn right_pad_with(&self, size: usize, pad_char: char) -> String;
#[must_use]
fn right_pad_with_str(&self, size: usize, pad_str: &str) -> String;
#[must_use]
fn center(&self, size: usize) -> String;
#[must_use]
fn center_with(&self, size: usize, pad_char: char) -> String;
#[must_use]
fn center_with_str(&self, size: usize, pad_str: &str) -> String;
#[must_use]
fn repeat_str(&self, times: usize) -> String;
#[must_use]
fn repeat_with_separator(&self, separator: &str, times: usize) -> String;
}
#[must_use]
pub fn repeat_char(c: char, times: usize) -> String {
if times == 0 {
return String::new();
}
std::iter::repeat_n(c, times).collect()
}
impl StringPad for str {
fn left_pad(&self, size: usize) -> String {
self.left_pad_with(size, ' ')
}
fn left_pad_with(&self, size: usize, pad_char: char) -> String {
let str_len = self.chars().count();
let pads = size.saturating_sub(str_len);
if pads == 0 {
return self.to_string();
}
let mut result = String::with_capacity(size);
for _ in 0..pads {
result.push(pad_char);
}
result.push_str(self);
result
}
fn left_pad_with_str(&self, size: usize, pad_str: &str) -> String {
let pad_str = if pad_str.is_empty() { " " } else { pad_str };
let str_len = self.chars().count();
let pads = size.saturating_sub(str_len);
if pads == 0 {
return self.to_string();
}
let pad_len = pad_str.chars().count();
let mut result = String::with_capacity(size);
let pad_chars: Vec<_> = pad_str.chars().collect();
for i in 0..pads {
result.push(pad_chars[i % pad_len]);
}
result.push_str(self);
result
}
fn right_pad(&self, size: usize) -> String {
self.right_pad_with(size, ' ')
}
fn right_pad_with(&self, size: usize, pad_char: char) -> String {
let str_len = self.chars().count();
let pads = size.saturating_sub(str_len);
if pads == 0 {
return self.to_string();
}
let mut result = String::with_capacity(size);
result.push_str(self);
for _ in 0..pads {
result.push(pad_char);
}
result
}
fn right_pad_with_str(&self, size: usize, pad_str: &str) -> String {
let pad_str = if pad_str.is_empty() { " " } else { pad_str };
let str_len = self.chars().count();
let pads = size.saturating_sub(str_len);
if pads == 0 {
return self.to_string();
}
let pad_len = pad_str.chars().count();
let mut result = String::with_capacity(size);
result.push_str(self);
let pad_chars: Vec<_> = pad_str.chars().collect();
for i in 0..pads {
result.push(pad_chars[i % pad_len]);
}
result
}
fn center(&self, size: usize) -> String {
self.center_with(size, ' ')
}
fn center_with(&self, size: usize, pad_char: char) -> String {
let str_len = self.chars().count();
let pads = size.saturating_sub(str_len);
if pads == 0 {
return self.to_string();
}
let left_pads = pads / 2;
let right_pads = pads - left_pads;
let mut result = String::with_capacity(size);
for _ in 0..left_pads {
result.push(pad_char);
}
result.push_str(self);
for _ in 0..right_pads {
result.push(pad_char);
}
result
}
fn center_with_str(&self, size: usize, pad_str: &str) -> String {
let pad_str = if pad_str.is_empty() { " " } else { pad_str };
let str_len = self.chars().count();
let pads = size.saturating_sub(str_len);
if pads == 0 {
return self.to_string();
}
let left_pads = pads / 2;
let right_pads = pads - left_pads;
let pad_len = pad_str.chars().count();
let pad_chars: Vec<char> = pad_str.chars().collect();
let mut result = String::with_capacity(size);
for i in 0..left_pads {
result.push(pad_chars[i % pad_len]);
}
result.push_str(self);
for i in 0..right_pads {
result.push(pad_chars[i % pad_len]);
}
result
}
fn repeat_str(&self, times: usize) -> String {
if times == 0 {
return String::new();
}
self.repeat(times)
}
fn repeat_with_separator(&self, separator: &str, times: usize) -> String {
if times == 0 {
return String::new();
}
let mut result = String::new();
for i in 0..times {
if i > 0 {
result.push_str(separator);
}
result.push_str(self);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
mod left_pad {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".left_pad(5), " ");
}
#[test]
fn pads_when_shorter() {
assert_eq!("abc".left_pad(5), " abc");
}
#[test]
fn no_pad_when_equal() {
assert_eq!("abc".left_pad(3), "abc");
}
#[test]
fn no_pad_when_longer() {
assert_eq!("abc".left_pad(2), "abc");
}
#[test]
fn zero_size() {
assert_eq!("abc".left_pad(0), "abc");
}
}
mod left_pad_with {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".left_pad_with(5, ' '), " ");
}
#[test]
fn pads_with_space() {
assert_eq!("abc".left_pad_with(5, ' '), " abc");
}
#[test]
fn pads_with_char() {
assert_eq!("abc".left_pad_with(5, 'x'), "xxabc");
}
#[test]
fn pads_with_unicode() {
assert_eq!("abc".left_pad_with(5, '\u{ffff}'), "\u{ffff}\u{ffff}abc");
}
#[test]
fn no_pad_when_longer() {
assert_eq!("abc".left_pad_with(2, ' '), "abc");
}
#[test]
fn large_string() {
let result = "aaa".left_pad_with(10000, 'a');
assert_eq!(result.len(), 10000);
assert!(result.chars().all(|c| c == 'a'));
}
}
mod left_pad_with_str {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".left_pad_with_str(5, " "), " ");
}
#[test]
fn pads_with_repeating_pattern() {
assert_eq!("abc".left_pad_with_str(7, "-+"), "-+-+abc");
}
#[test]
fn pads_with_partial_pattern() {
assert_eq!("abc".left_pad_with_str(6, "-+~"), "-+~abc");
}
#[test]
fn pads_with_truncated_pattern() {
assert_eq!("abc".left_pad_with_str(5, "-+~"), "-+abc");
}
#[test]
fn no_pad_when_longer() {
assert_eq!("abc".left_pad_with_str(2, " "), "abc");
}
#[test]
fn empty_pad_str_uses_space() {
assert_eq!("abc".left_pad_with_str(5, ""), " abc");
}
}
mod right_pad {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".right_pad(5), " ");
}
#[test]
fn pads_when_shorter() {
assert_eq!("abc".right_pad(5), "abc ");
}
#[test]
fn no_pad_when_equal() {
assert_eq!("abc".right_pad(3), "abc");
}
#[test]
fn no_pad_when_longer() {
assert_eq!("abc".right_pad(2), "abc");
}
}
mod right_pad_with {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".right_pad_with(5, ' '), " ");
}
#[test]
fn pads_with_space() {
assert_eq!("abc".right_pad_with(5, ' '), "abc ");
}
#[test]
fn pads_with_char() {
assert_eq!("abc".right_pad_with(5, 'x'), "abcxx");
}
#[test]
fn pads_with_unicode() {
assert_eq!("abc".right_pad_with(5, '\u{ffff}'), "abc\u{ffff}\u{ffff}");
}
#[test]
fn no_pad_when_longer() {
assert_eq!("abc".right_pad_with(2, ' '), "abc");
}
#[test]
fn large_string() {
let result = "aaa".right_pad_with(10000, 'a');
assert_eq!(result.len(), 10000);
assert!(result.chars().all(|c| c == 'a'));
}
}
mod right_pad_with_str {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".right_pad_with_str(5, " "), " ");
}
#[test]
fn pads_with_repeating_pattern() {
assert_eq!("abc".right_pad_with_str(7, "-+"), "abc-+-+");
}
#[test]
fn pads_with_partial_pattern() {
assert_eq!("abc".right_pad_with_str(6, "-+~"), "abc-+~");
}
#[test]
fn pads_with_truncated_pattern() {
assert_eq!("abc".right_pad_with_str(5, "-+~"), "abc-+");
}
#[test]
fn no_pad_when_longer() {
assert_eq!("abc".right_pad_with_str(2, " "), "abc");
}
#[test]
fn empty_pad_str_uses_space() {
assert_eq!("abc".right_pad_with_str(5, ""), "abc ");
}
}
mod center {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".center(4), " ");
}
#[test]
fn zero_size() {
assert_eq!("ab".center(0), "ab");
}
#[test]
fn size_smaller_than_string() {
assert_eq!("ab".center(1), "ab");
}
#[test]
fn even_padding() {
assert_eq!("ab".center(4), " ab ");
}
#[test]
fn string_longer_than_size() {
assert_eq!("abcd".center(2), "abcd");
}
#[test]
fn odd_padding_extra_right() {
assert_eq!("a".center(4), " a ");
}
#[test]
fn odd_length_even_padding() {
assert_eq!("a".center(5), " a ");
}
}
mod center_with {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".center_with(4, ' '), " ");
}
#[test]
fn zero_size() {
assert_eq!("ab".center_with(0, ' '), "ab");
}
#[test]
fn even_padding() {
assert_eq!("ab".center_with(4, ' '), " ab ");
}
#[test]
fn with_custom_char() {
assert_eq!("a".center_with(5, 'x'), "xxaxx");
}
}
mod center_with_str {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".center_with_str(4, " "), " ");
}
#[test]
fn even_padding() {
assert_eq!("ab".center_with_str(4, " "), " ab ");
}
#[test]
fn with_pattern_even() {
assert_eq!("a".center_with_str(4, "yz"), "yayz");
}
#[test]
fn with_pattern_longer() {
assert_eq!("a".center_with_str(7, "yz"), "yzyayzy");
}
#[test]
fn empty_pad_str_uses_space() {
assert_eq!("abc".center_with_str(7, ""), " abc ");
}
}
mod repeat_char_fn {
use super::*;
#[test]
fn repeats_char() {
assert_eq!(repeat_char('z', 3), "zzz");
}
#[test]
fn zero_times() {
assert_eq!(repeat_char('z', 0), "");
}
}
mod repeat_str {
use super::*;
#[test]
fn zero_times() {
assert_eq!("ab".repeat_str(0), "");
}
#[test]
fn empty_string() {
assert_eq!("".repeat_str(3), "");
}
#[test]
fn single_char() {
assert_eq!("a".repeat_str(3), "aaa");
}
#[test]
fn multiple_chars() {
assert_eq!("ab".repeat_str(3), "ababab");
}
#[test]
fn three_chars() {
assert_eq!("abc".repeat_str(3), "abcabcabc");
}
#[test]
fn large_string() {
let result = "a".repeat_str(10000);
assert_eq!(result.len(), 10000);
assert!(result.chars().all(|c| c == 'a'));
}
}
mod repeat_with_separator {
use super::*;
#[test]
fn empty_string_with_separator() {
assert_eq!("".repeat_with_separator("x", 3), "xx");
}
#[test]
fn zero_times() {
assert_eq!("ab".repeat_with_separator("", 0), "");
}
#[test]
fn empty_string_empty_separator() {
assert_eq!("".repeat_with_separator("", 2), "");
}
#[test]
fn with_comma_separator() {
assert_eq!("?".repeat_with_separator(", ", 3), "?, ?, ?");
}
}
mod string_types {
use super::*;
#[test]
fn string_type() {
assert_eq!(String::from("abc").left_pad(5), " abc");
}
#[test]
fn string_ref() {
let s = String::from("abc");
assert_eq!(s.right_pad(5), "abc ");
}
}
}