Reorganize
This commit is contained in:
136
crates/lib/toolbox/src/strings.rs
Normal file
136
crates/lib/toolbox/src/strings.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use num::{Integer, Zero, cast::AsPrimitive};
|
||||
|
||||
/// Pretty-print a quantity of bytes
|
||||
pub fn pp_bytes_si<T: Integer + Display + Zero + Clone + AsPrimitive<f64>>(bytes: T) -> String {
|
||||
const UNITS: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
|
||||
const THRESHOLD: f64 = 1000.0;
|
||||
|
||||
if bytes.is_zero() {
|
||||
return "0 B".to_owned();
|
||||
}
|
||||
|
||||
let bytes_f: f64 = bytes.as_();
|
||||
let unit_index = (bytes_f.log10() / THRESHOLD.log10()).floor() as usize;
|
||||
let unit_index = unit_index.min(UNITS.len() - 1);
|
||||
|
||||
let value = bytes_f / THRESHOLD.powi(unit_index as i32);
|
||||
|
||||
if unit_index == 0 {
|
||||
format!("{} {}", bytes, UNITS[unit_index])
|
||||
} else if value >= 100.0 {
|
||||
format!("{:.0} {}", value, UNITS[unit_index])
|
||||
} else if value >= 10.0 {
|
||||
format!("{:.1} {}", value, UNITS[unit_index])
|
||||
} else {
|
||||
format!("{:.2} {}", value, UNITS[unit_index])
|
||||
}
|
||||
}
|
||||
|
||||
/// Pretty-print a quantity of bytes
|
||||
pub fn pp_bytes_b2<T: Integer + Display + Zero + Clone + AsPrimitive<f64>>(bytes: T) -> String {
|
||||
const UNITS: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
|
||||
const THRESHOLD: f64 = 1024.0;
|
||||
|
||||
if bytes.is_zero() {
|
||||
return "0 B".to_owned();
|
||||
}
|
||||
|
||||
let bytes_f: f64 = bytes.as_();
|
||||
let unit_index = (bytes_f.log2() / THRESHOLD.log2()).floor() as usize;
|
||||
let unit_index = unit_index.min(UNITS.len() - 1);
|
||||
|
||||
let value = bytes_f / THRESHOLD.powi(unit_index as i32);
|
||||
|
||||
if unit_index == 0 {
|
||||
format!("{} {}", bytes, UNITS[unit_index])
|
||||
} else if value >= 100.0 {
|
||||
format!("{:.0} {}", value, UNITS[unit_index])
|
||||
} else if value >= 10.0 {
|
||||
format!("{:.1} {}", value, UNITS[unit_index])
|
||||
} else {
|
||||
format!("{:.2} {}", value, UNITS[unit_index])
|
||||
}
|
||||
}
|
||||
|
||||
/// Pretty-print an integer with comma separators
|
||||
pub fn pp_int_commas<T: Integer + Display>(value: T) -> String {
|
||||
let s = value.to_string();
|
||||
let mut result = String::new();
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for (i, &byte) in bytes.iter().enumerate() {
|
||||
if i > 0 && (bytes.len() - i).is_multiple_of(3) {
|
||||
result.push(',');
|
||||
}
|
||||
result.push(byte as char);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Convert bytes to a hex string
|
||||
#[inline]
|
||||
pub fn to_hex(bytes: &[u8]) -> String {
|
||||
bytes.iter().map(|b| format!("{:02x}", b)).collect()
|
||||
}
|
||||
|
||||
/// Parse a hex string as bytes
|
||||
pub fn from_hex(hex_str: &str) -> Result<Vec<u8>, &'static str> {
|
||||
if !hex_str.len().is_multiple_of(2) {
|
||||
return Err("hex string must have even length");
|
||||
}
|
||||
|
||||
let mut bytes = Vec::with_capacity(hex_str.len() / 2);
|
||||
|
||||
for chunk in hex_str.as_bytes().chunks(2) {
|
||||
let hex_byte = std::str::from_utf8(chunk).map_err(|_err| "invalid UTF-8 in hex string")?;
|
||||
let byte = u8::from_str_radix(hex_byte, 16).map_err(|_err| "invalid hex character")?;
|
||||
bytes.push(byte);
|
||||
}
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Truncate a string from the left, replacing the first character with "…" if truncated
|
||||
pub fn truncate_left(length: usize, string: &str) -> String {
|
||||
if string.chars().count() <= length {
|
||||
return string.to_owned();
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if length == 1 {
|
||||
return "…".to_owned();
|
||||
}
|
||||
|
||||
let chars: Vec<char> = string.chars().collect();
|
||||
let start_index = chars.len() - (length - 1);
|
||||
let mut result = String::from("…");
|
||||
result.extend(&chars[start_index..]);
|
||||
result
|
||||
}
|
||||
|
||||
/// Truncate a string from the right, replacing the last character with "…" if truncated
|
||||
pub fn truncate_right(length: usize, string: &str) -> String {
|
||||
if string.chars().count() <= length {
|
||||
return string.to_owned();
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if length == 1 {
|
||||
return "…".to_owned();
|
||||
}
|
||||
|
||||
let chars: Vec<char> = string.chars().collect();
|
||||
let mut result = String::new();
|
||||
result.extend(&chars[0..length - 1]);
|
||||
result.push('…');
|
||||
result
|
||||
}
|
||||
Reference in New Issue
Block a user