use rand::Rng;
use std::time::Duration;
use unicode_segmentation::UnicodeSegmentation;
pub fn truncate_message(message: &str, max_width: usize) -> String {
assert!(
max_width >= 2,
"max_width must be at least 2 to accommodate the ellipsis"
);
let ellipsis = "β¦";
let graphemes: Vec<&str> = message.graphemes(true).collect();
if graphemes.len() > max_width {
let truncated: String = graphemes[..max_width - 1].concat();
format!("{}{}", truncated, ellipsis)
} else {
message.to_string()
}
}
pub fn generate_random_number(length: u32) -> u64 {
assert!(length > 0, "length must be greater than 0");
let range = 10u64.pow(length - 1)..10u64.pow(length);
rand::rng().random_range(range)
}
pub fn validate_basic_auth(val: &str) -> Result<String, String> {
if val.contains(':') {
let parts: Vec<&str> = val.splitn(2, ':').collect();
if parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty() {
Ok(val.to_string())
} else {
Err(String::from(
"Invalid format: must be `username:password` with non-empty values",
))
}
} else {
Err(String::from("Invalid format: must be `username:password`"))
}
}
pub fn kb(bytes: usize) -> String {
let kilobytes = bytes as f64 / 1024.0;
format!("{kilobytes:.2}kb")
}
pub fn percent(percent: f64) -> String {
format!("{percent:.0}%")
}
pub fn ms(duration: Duration) -> String {
let milliseconds = duration.as_millis() as f64;
format!("{milliseconds:.2}ms")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_message_no_truncation_needed() {
assert_eq!(truncate_message("Short", 10), "Short");
}
#[test]
fn test_truncate_message_with_truncation() {
let result = truncate_message("This is a long message", 10);
assert_eq!(result, "This is aβ¦");
}
#[test]
fn test_truncate_message_exact_width() {
let result = truncate_message("Exact size", 10);
assert_eq!(result, "Exact size");
}
#[test]
fn test_truncate_message_empty_string() {
let message = "";
let result = truncate_message(message, 5);
assert_eq!(result, "");
}
#[test]
#[should_panic(expected = "max_width must be at least 2 to accommodate the ellipsis")]
fn test_truncate_message_max_width_one() {
truncate_message("Something", 1);
}
#[test]
fn test_truncate_message_unicode_handling() {
let message = "γγγ«γ‘γ―δΈη"; let result = truncate_message(message, 6);
assert_eq!(result, "γγγ«γ‘γ―β¦");
}
#[test]
fn test_truncate_message_emoji_handling() {
let message = "π½βπ§πΌββοΈπ«π€£π¨";
let result = truncate_message(message, 3);
assert_eq!(result, "π½βπ§πΌββοΈπ«β¦");
}
#[test]
fn test_valid_basic_auth() {
assert!(validate_basic_auth("user:pass").is_ok());
assert!(validate_basic_auth("user@domain.com:password123").is_ok());
assert!(validate_basic_auth("user:pass:with:colon").is_ok());
}
#[test]
fn test_invalid_basic_auth() {
assert!(validate_basic_auth("invalid").is_err());
assert!(validate_basic_auth("").is_err());
assert!(validate_basic_auth(":").is_err());
assert!(validate_basic_auth("user:").is_err());
assert!(validate_basic_auth(":pass").is_err());
}
use std::collections::HashSet;
#[test]
fn test_generate_random_number_valid_length() {
let length = 5;
let number = generate_random_number(length);
let number_str = number.to_string();
assert_eq!(
number_str.len(),
length as usize,
"Generated number does not match the specified length"
);
}
#[test]
fn test_generate_random_number_min_length() {
let length = 1;
let number = generate_random_number(length);
assert!(
(1..=9).contains(&number),
"Generated number is outside the range for length 1"
);
}
#[test]
fn test_generate_random_number_randomness() {
let length = 4;
let mut numbers = HashSet::new();
for _ in 0..1000 {
let number = generate_random_number(length);
numbers.insert(number);
}
assert!(
numbers.len() > 900,
"Generated numbers are not sufficiently random (too many duplicates found)"
);
}
#[test]
#[should_panic(expected = "length must be greater than 0")]
fn test_generate_random_number_zero_length() {
generate_random_number(0);
}
}