Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] document and improve padding functionality #6

Merged
merged 3 commits into from Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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());
}
}