use once_cell::sync::Lazy;
use std::ops::AddAssign;
static EMPTY_DATA: Lazy<Vec<u8>> = Lazy::new(Vec::new);
#[derive(Clone, Debug)]
pub struct StringJoiner {
prefix: Option<Vec<u8>>,
suffix: Option<Vec<u8>>,
delimiter: Option<Vec<u8>>,
data: Vec<u8>,
}
impl StringJoiner {
pub fn new(prefix: Option<&str>, suffix: Option<&str>, delimiter: Option<&str>) -> Self {
Self {
prefix: prefix.map(|s| Vec::from(s.as_bytes())),
suffix: suffix.map(|s| Vec::from(s.as_bytes())),
delimiter: delimiter.map(|s| Vec::from(s.as_bytes())),
data: match prefix {
None => Vec::new(),
Some(prefix) => Vec::from(prefix.as_bytes()),
},
}
}
pub fn with_capacity(capacity: usize, prefix: Option<&str>, suffix: Option<&str>, delimiter: Option<&str>) -> Self {
Self {
prefix: prefix.map(|s| Vec::from(s.as_bytes())),
suffix: suffix.map(|s| Vec::from(s.as_bytes())),
delimiter: delimiter.map(|s| Vec::from(s.as_bytes())),
data: match prefix {
None => Vec::with_capacity(capacity),
Some(prefix) => {
let mut data = Vec::with_capacity(capacity);
data.extend_from_slice(prefix.as_bytes());
data
}
},
}
}
pub fn push(&mut self, c: char) -> &mut Self {
*self += String::from(c);
self
}
pub fn push_str(&mut self, str: &str) -> &mut Self {
*self += str;
self
}
pub fn push_string(&mut self, s: String) -> &mut Self {
*self += s;
self
}
pub fn push_string_joiner(&mut self, other: StringJoiner) -> &mut Self {
*self += other.into_string();
self
}
pub fn into_string(self) -> String {
let buf = self.into_vec();
String::from_utf8_lossy(buf.as_slice()).to_string()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty() || Some(&self.data) == self.prefix.as_ref()
}
pub fn into_vec(self) -> Vec<u8> {
let empty_flag = self.is_empty();
let Self {
prefix: _,
suffix,
delimiter: _,
mut data,
} = self;
match empty_flag {
true => Vec::new(),
false => match suffix {
None => data,
Some(mut suffix) => {
data.append(&mut suffix);
data
}
},
}
}
pub fn to_vec(&self) -> Vec<u8> {
let prefix = self.prefix.as_ref().unwrap_or_else(|| EMPTY_DATA.as_ref());
match self.data.is_empty() || self.data == *prefix {
true => Vec::new(),
false => match self.suffix {
None => self.data.clone(),
Some(ref suffix) => {
let mut len = suffix.len();
len += self.data.len();
let mut tmp = Vec::with_capacity(len);
tmp.extend_from_slice(self.data.as_ref());
tmp.extend_from_slice(suffix.as_slice());
tmp
}
},
}
}
}
impl<S: AsRef<str>> AddAssign<S> for StringJoiner {
fn add_assign(&mut self, rhs: S) {
if let Some(delimiter) = self.delimiter.as_ref() {
let prefix = self.prefix.as_ref().unwrap_or_else(|| EMPTY_DATA.as_ref());
if !self.data.is_empty() && *self.data != *prefix {
self.data.extend_from_slice(delimiter.as_ref());
}
}
self.data.extend_from_slice(rhs.as_ref().as_bytes());
}
}
impl AddAssign<StringJoiner> for StringJoiner {
fn add_assign(&mut self, rhs: StringJoiner) {
*self += rhs.into_string();
}
}
impl ToString for StringJoiner {
fn to_string(&self) -> String {
String::from_utf8_lossy(self.to_vec().as_slice()).to_string()
}
}
#[cfg(test)]
mod test {
use crate::tina::util::string_joiner::StringJoiner;
#[test]
fn test_string_joiner() {
let sj = StringJoiner::new(Some("("), Some(")"), Some(","));
assert_eq!(sj.to_string(), String::new());
assert_eq!(sj.into_string(), String::new());
let mut sj = StringJoiner::new(None, None, None);
sj += "A";
assert_eq!(sj.to_string(), String::from("A"));
assert_eq!(sj.into_string(), String::from("A"));
let mut sj = StringJoiner::new(None, None, None);
sj += "A";
assert_eq!(sj.to_string(), String::from("A"));
sj += "B";
assert_eq!(sj.into_string(), String::from("AB"));
let mut sj = StringJoiner::new(None, None, Some(","));
sj += "A";
assert_eq!(sj.to_string(), String::from("A"));
sj += "B";
assert_eq!(sj.into_string(), String::from("A,B"));
let mut sj = StringJoiner::new(Some("("), None, Some(","));
sj += "A";
assert_eq!(sj.to_string(), String::from("(A"));
assert_eq!(sj.into_string(), String::from("(A"));
let mut sj = StringJoiner::new(None, Some(")"), Some(","));
sj += "A";
assert_eq!(sj.to_string(), String::from("A)"));
assert_eq!(sj.into_string(), String::from("A)"));
let mut sj = StringJoiner::new(Some("("), Some(")"), Some(","));
sj += "A";
assert_eq!(sj.to_string(), String::from("(A)"));
assert_eq!(sj.into_string(), String::from("(A)"));
let mut sj = StringJoiner::new(Some("("), Some(")"), Some(","));
sj += "A";
sj += "B";
assert_eq!(sj.to_string(), String::from("(A,B)"));
assert_eq!(sj.into_string(), String::from("(A,B)"));
}
}