Skip to content

Commit

Permalink
[feat] document and improve padding functionality (#6)
Browse files Browse the repository at this point in the history
* [bench] update padding benchmarks with unwraps

* [feat] add slice to fit functionality and logging

* [build] logging deps
  • Loading branch information
wilhelmagren committed Dec 14, 2023
1 parent 2a3d6be commit 340e8be
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 35 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Expand Up @@ -27,6 +27,8 @@ bench = true
crate-type = [ "lib" ]

[dependencies]
env_logger = "0.10.1"
log = "0.4.20"

[dev-dependencies]
criterion = "0.5.1"
Expand Down
23 changes: 11 additions & 12 deletions benches/benchmarks/pad_whitespace_leftalign.rs
Expand Up @@ -5,26 +5,22 @@ use padder::Alignment;
pub fn pad_whitespace_10_leftalign(c: &mut Criterion) {
let width: usize = 10;
c.bench_function("pad ws 10 la", |b| {
b.iter(|| black_box(whitespace("hej", width, Alignment::Left)))
b.iter(|| black_box(whitespace("hej", width, Alignment::Left).unwrap()))
});
}

pub fn pad_whitespace_100_leftalign(c: &mut Criterion) {
let width: usize = 100;
c.bench_function("pad ws 100 la", |b| {
b.iter(|| black_box(whitespace("bingbong", width, Alignment::Left)))
b.iter(|| black_box(whitespace("bingbong", width, Alignment::Left).unwrap()))
});
}

pub fn pad_whitespace_1000_leftalign(c: &mut Criterion) {
let width: usize = 1000;
c.bench_function("pad ws 1000 la", |b| {
b.iter(|| {
black_box(whitespace(
"Undercity is a cool capital...",
width,
Alignment::Left,
))
black_box(whitespace("Undercity is a cool capital...", width, Alignment::Left).unwrap())
})
});
}
Expand All @@ -33,11 +29,14 @@ pub fn pad_whitespace_10000_leftalign(c: &mut Criterion) {
let width: usize = 10000;
c.bench_function("pad ws 10000 la", |b| {
b.iter(|| {
black_box(whitespace(
"¤)(åäöåa this is a very long string... xd",
width,
Alignment::Left,
))
black_box(
whitespace(
"¤)(åäöåa this is a very long string... xd",
width,
Alignment::Left,
)
.unwrap(),
)
})
});
}
Expand Down
152 changes: 129 additions & 23 deletions src/lib.rs
Expand Up @@ -25,6 +25,7 @@
* Last updated: 2023-12-14
*/

use log::warn;
use std::fmt;

///
Expand All @@ -36,31 +37,37 @@ impl fmt::Display for WidthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Invalid target pad width for the provided string length."
"invalid target pad width for the provided string length."
)
}
}

///
#[derive(Debug)]
pub enum Alignment {
Left,
Right,
Center,
}

///
pub fn whitespace(string: &str, width: usize, mode: Alignment) -> String {
pad(string, width, mode, ' ')
pub fn whitespace(string: &str, width: usize, mode: Alignment) -> Result<String, WidthError> {
pad(string, width, &mode, ' ')
}

///
pub fn zeros(string: &str, width: usize, mode: Alignment) -> String {
pad(string, width, mode, '0')
pub fn zeros(string: &str, width: usize, mode: Alignment) -> Result<String, WidthError> {
pad(string, width, &mode, '0')
}

///
pub fn pad_into_bytes(string: &str, width: usize, mode: Alignment, pad_char: char) -> Vec<u8> {
pad(string, width, mode, pad_char).into_bytes()
pub fn pad_into_bytes(
string: &str,
width: usize,
mode: Alignment,
pad_char: char,
) -> Result<Vec<u8>, WidthError> {
Ok(pad(string, width, &mode, pad_char)?.into_bytes())
}

///
Expand All @@ -71,22 +78,44 @@ pub fn pad_and_push_to_buffer(
pad_char: char,
buffer: &mut Vec<u8>,
) {
buffer.extend_from_slice(pad(string, width, mode, pad_char).as_bytes());
let padded: String = match pad(string, width, &mode, pad_char) {
Ok(s) => s,
Err(e) => {
warn!("could not pad `{}` due to: {}", string, e);
warn!("will slice the string to fit in the buffer, expect some data to be missing...");
slice_to_fit(string, width, &mode).to_string()
}
};

buffer.extend_from_slice(padded.as_bytes());
}

///
/// # Panic
/// Iff the [`pad`] function returns a [`WidthError`], at which point the string can't be padded.
pub fn try_pad_and_push_to_buffer(
string: &str,
width: usize,
mode: Alignment,
pad_char: char,
buffer: &mut Vec<u8>,
) {
buffer.extend_from_slice(pad(string, width, &mode, pad_char).unwrap().as_bytes());
}

///
/// Panics
/// Iff the target pad width is less than the provided string length.
fn pad(string: &str, width: usize, mode: Alignment, pad_char: char) -> String {
/// # Error
/// Iff the target padding width is less than the length of the string to pad.
fn pad(string: &str, width: usize, mode: &Alignment, pad_char: char) -> Result<String, WidthError> {
if width < string.len() {
panic!("Invalid target pad width for the provide string length.")
return Err(WidthError);
}

let mut output = String::with_capacity(width);
let diff: usize = width - string.len();

if diff == 0 {
return string.to_string();
return Ok(string.to_string());
}

let (lpad, rpad) = match mode {
Expand All @@ -99,25 +128,102 @@ fn pad(string: &str, width: usize, mode: Alignment, pad_char: char) -> String {
output.push_str(string);
(0..rpad).for_each(|_| output.push(pad_char));

output
Ok(output)
}

///
fn slice_to_fit<'a>(string: &'a str, width: usize, mode: &'a Alignment) -> &'a str {
match mode {
Alignment::Left => &string[0..width],
Alignment::Right => &string[(string.len() - width)..],
Alignment::Center => {
&string[(string.len() / 2 - width / 2)..(string.len() / 2 + width / 2)]
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
#[should_panic]
fn pad_unwrap_on_error() {
let mut buffer: Vec<u8> = Vec::with_capacity(193838);
try_pad_and_push_to_buffer(
"the length of this string is much larger than the target padding width",
10,
Alignment::Right,
' ',
&mut buffer,
);
}

#[test]
fn pad_and_slice_to_fit_left_align() {
let mut buffer: Vec<u8> = Vec::with_capacity(193838);
pad_and_push_to_buffer(
"the length of this string is much larger than the target padding width",
10,
Alignment::Left,
' ',
&mut buffer,
);

assert_eq!("the length".as_bytes(), buffer);
}

#[test]
fn pad_and_slice_to_fit_right_align() {
let mut buffer: Vec<u8> = Vec::with_capacity(193838);
pad_and_push_to_buffer(
"the length of this string is much larger than the target padding width",
10,
Alignment::Right,
' ',
&mut buffer,
);

assert_eq!("ding width".as_bytes(), buffer);
}

#[test]
fn pad_and_slice_to_fit_center_align_even() {
let mut buffer: Vec<u8> = Vec::with_capacity(1024);
pad_and_push_to_buffer("hejjag", 2, Alignment::Center, ' ', &mut buffer);

assert_eq!("jj".as_bytes(), buffer);
}

#[test]
fn pad_and_slice_to_fit_center_align_uneven() {
let mut buffer: Vec<u8> = Vec::with_capacity(1024);
pad_and_push_to_buffer(
"a little bit trickier center align!",
10,
Alignment::Center,
' ',
&mut buffer,
);

assert_eq!(" trickier ".as_bytes(), buffer);
}

#[test]
#[should_panic]
fn pad_should_panic() {
let _ = whitespace("lol hahahah xd test", 15, Alignment::Center);
let _ = whitespace("lol hahahah xd test", 15, Alignment::Center).unwrap();
}

#[test]
fn pad_whitespace_right_align_into_bytes() {
let width: usize = 30;
let bytes = pad_into_bytes("this is cool", width, Alignment::Right, ' ');

assert_eq!(format!("{:>width$}", "this is cool").into_bytes(), bytes,);
assert_eq!(
format!("{:>width$}", "this is cool").into_bytes(),
bytes.unwrap()
);
}

#[test]
Expand All @@ -144,54 +250,54 @@ mod tests {
let width: usize = 30;
let pad = whitespace("this is cool", width, Alignment::Left);

assert_eq!(format!("{:<width$}", "this is cool"), pad);
assert_eq!(format!("{:<width$}", "this is cool"), pad.unwrap());
}

#[test]
fn pad_whitespace_right_align() {
let width: usize = 30;
let pad = whitespace("this is cool", width, Alignment::Right);

assert_eq!(format!("{:>width$}", "this is cool"), pad);
assert_eq!(format!("{:>width$}", "this is cool"), pad.unwrap());
}

#[test]
fn pad_whitespace_center_align() {
let width: usize = 30;
let pad = whitespace("this is cool", width, Alignment::Center);

assert_eq!(format!("{:^width$}", "this is cool"), pad);
assert_eq!(format!("{:^width$}", "this is cool"), pad.unwrap());
}

#[test]
fn pad_zeros_left_align() {
let width: usize = 30;
let pad = zeros("this is cool", width, Alignment::Left);

assert_eq!(format!("{:0<width$}", "this is cool"), pad);
assert_eq!(format!("{:0<width$}", "this is cool"), pad.unwrap());
}

#[test]
fn pad_zeros_right_align() {
let width: usize = 30;
let pad = zeros("this is cool", width, Alignment::Right);

assert_eq!(format!("{:0>width$}", "this is cool"), pad);
assert_eq!(format!("{:0>width$}", "this is cool"), pad.unwrap());
}

#[test]
fn pad_zeros_center_align() {
let width: usize = 30;
let pad = zeros("this is cool", width, Alignment::Center);

assert_eq!(format!("{:0^width$}", "this is cool"), pad);
assert_eq!(format!("{:0^width$}", "this is cool"), pad.unwrap());
}

#[test]
fn pad_1000000_zeros_center() {
let width: usize = 100_000_000;
let pad = zeros("this is cool", width, Alignment::Center);

assert_eq!(format!("{:0^width$}", "this is cool"), pad);
assert_eq!(format!("{:0^width$}", "this is cool"), pad.unwrap());
}
}

0 comments on commit 340e8be

Please sign in to comment.