use crate::support::string_ref::StringRef;
use std::fmt;
#[derive(Debug, Clone)]
enum TwineKind<'a> {
Str(StringRef<'a>),
String(std::string::String),
Int(i64),
UInt(u64),
Char(char),
Concat(Box<Twine<'a>>, Box<Twine<'a>>),
Null,
}
#[derive(Debug, Clone)]
pub struct Twine<'a> {
kind: TwineKind<'a>,
}
impl<'a> Twine<'a> {
pub fn null() -> Self {
Self {
kind: TwineKind::Null,
}
}
pub fn is_null(&self) -> bool {
matches!(self.kind, TwineKind::Null)
}
#[allow(clippy::inherent_to_string_shadow_display)]
pub fn to_string(&self) -> std::string::String {
let mut buf = std::string::String::new();
self.write_to(&mut buf);
buf
}
fn write_to(&self, buf: &mut std::string::String) {
match &self.kind {
TwineKind::Null => {}
TwineKind::Str(s) => buf.push_str(s.data()),
TwineKind::String(s) => buf.push_str(s),
TwineKind::Int(i) => buf.push_str(&i.to_string()),
TwineKind::UInt(u) => buf.push_str(&u.to_string()),
TwineKind::Char(c) => buf.push(*c),
TwineKind::Concat(a, b) => {
a.write_to(buf);
b.write_to(buf);
}
}
}
pub fn concat(self, other: Twine<'a>) -> Twine<'a> {
if self.is_null() {
return other;
}
if other.is_null() {
return self;
}
Twine {
kind: TwineKind::Concat(Box::new(self), Box::new(other)),
}
}
}
impl<'a> From<StringRef<'a>> for Twine<'a> {
fn from(s: StringRef<'a>) -> Self {
Self {
kind: TwineKind::Str(s),
}
}
}
impl<'a> From<&'a str> for Twine<'a> {
fn from(s: &'a str) -> Self {
Self {
kind: TwineKind::Str(StringRef::new(s)),
}
}
}
impl From<std::string::String> for Twine<'_> {
fn from(s: std::string::String) -> Self {
Self {
kind: TwineKind::String(s),
}
}
}
impl From<i64> for Twine<'_> {
fn from(i: i64) -> Self {
Self {
kind: TwineKind::Int(i),
}
}
}
impl From<i32> for Twine<'_> {
fn from(i: i32) -> Self {
Twine::from(i as i64)
}
}
impl From<u64> for Twine<'_> {
fn from(u: u64) -> Self {
Self {
kind: TwineKind::UInt(u),
}
}
}
impl From<u32> for Twine<'_> {
fn from(u: u32) -> Self {
Twine::from(u as u64)
}
}
impl From<usize> for Twine<'_> {
fn from(u: usize) -> Self {
Twine::from(u as u64)
}
}
impl From<char> for Twine<'_> {
fn from(c: char) -> Self {
Self {
kind: TwineKind::Char(c),
}
}
}
impl fmt::Display for Twine<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_twine_concat() {
let t = Twine::from("hello")
.concat(Twine::from(" "))
.concat(Twine::from("world"));
assert_eq!(t.to_string(), "hello world");
}
#[test]
fn test_twine_int() {
let t = Twine::from("n=").concat(Twine::from(42i64));
assert_eq!(t.to_string(), "n=42");
}
#[test]
fn test_twine_null() {
let t = Twine::null();
assert_eq!(t.to_string(), "");
}
#[test]
fn test_twine_char() {
let t = Twine::from('A').concat(Twine::from("BC"));
assert_eq!(t.to_string(), "ABC");
}
}