#[derive(Clone, Default, PartialEq, Eq)]
pub struct SparseText(Option<Box<str>>);
impl SparseText {
pub fn as_str(&self) -> &str {
self.0.as_deref().unwrap_or("")
}
pub fn is_empty(&self) -> bool {
self.0.is_none()
}
pub fn clear(&mut self) {
self.0 = None;
}
pub fn set(&mut self, text: &str) {
self.0 = (!text.is_empty()).then(|| Box::from(text));
}
fn eq_str(&self, other: &str) -> bool {
match &self.0 {
None => other.is_empty(),
Some(text) => **text == *other,
}
}
}
impl std::fmt::Debug for SparseText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self.as_str(), f)
}
}
impl std::fmt::Display for SparseText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SparseText {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<&str> for SparseText {
fn from(text: &str) -> Self {
Self((!text.is_empty()).then(|| Box::from(text)))
}
}
impl From<String> for SparseText {
fn from(text: String) -> Self {
Self((!text.is_empty()).then(|| text.into_boxed_str()))
}
}
impl PartialEq<str> for SparseText {
fn eq(&self, other: &str) -> bool {
self.eq_str(other)
}
}
impl PartialEq<&str> for SparseText {
fn eq(&self, other: &&str) -> bool {
self.eq_str(other)
}
}
impl PartialEq<SparseText> for &str {
fn eq(&self, other: &SparseText) -> bool {
other.eq_str(self)
}
}
impl PartialEq<String> for SparseText {
fn eq(&self, other: &String) -> bool {
self.eq_str(other)
}
}
#[cfg(test)]
mod tests {
use super::SparseText;
#[test]
fn an_empty_write_is_stored_as_absence() {
let mut text = SparseText::from("boom");
assert!(!text.is_empty());
text.set("");
assert!(text.is_empty(), "\"\" must not survive as an empty Some");
assert_eq!(text, "");
assert_eq!(text, SparseText::default());
assert_eq!(text, String::new());
}
#[test]
fn absence_and_text_compare_both_ways() {
let absent = SparseText::default();
let present = SparseText::from("field OUT".to_string());
assert_ne!(absent, present);
assert_eq!(present, "field OUT");
assert_ne!(present, "field INP");
assert_ne!(present, String::new());
assert_eq!(absent, String::new());
assert_eq!(absent.as_str(), "");
}
}