pub trait Blank {
fn is_blank(&self) -> bool;
fn is_present(&self) -> bool {
!self.is_blank()
}
}
impl Blank for str {
fn is_blank(&self) -> bool {
self.trim().is_empty()
}
}
impl<T: Blank + ?Sized> Blank for &T {
fn is_blank(&self) -> bool {
(**self).is_blank()
}
}
impl Blank for String {
fn is_blank(&self) -> bool {
self.as_str().is_blank()
}
}
impl<T: Blank> Blank for Option<T> {
fn is_blank(&self) -> bool {
self.as_ref().map(Blank::is_blank).unwrap_or(true)
}
}
impl<T> Blank for Vec<T> {
fn is_blank(&self) -> bool {
self.is_empty()
}
}
pub trait StringExt {
fn truncate_chars(&self, len: usize) -> String;
}
impl StringExt for str {
fn truncate_chars(&self, len: usize) -> String {
if self.chars().count() <= len {
self.to_string()
} else {
let kept: String = self.chars().take(len.saturating_sub(1)).collect();
format!("{kept}…")
}
}
}