use core::{
fmt::{Debug, Display},
hash::Hash,
ops::Deref,
};
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::string::ToString;
use alloc::sync::Arc;
use crate::codec::{
CodecError, DataHeader, Decodable, Encodable, Format, UnexpectedDataFormatSnafu,
WritesEncodable,
};
pub enum Text {
Static(&'static str),
Dynamic(Arc<String>),
}
impl Text {
pub const EMPTY: Text = Text::Static("");
pub const fn from(s: &'static str) -> Self {
Text::Static(s)
}
pub fn as_str(&self) -> &str {
match self {
Text::Static(t) => t,
Text::Dynamic(t) => t.as_str(),
}
}
pub fn to_mut(&mut self) -> &mut String {
match self {
Text::Static(t) => {
*self = Text::Dynamic(t.to_string().into());
self.to_mut()
}
Text::Dynamic(t) => Arc::make_mut(t),
}
}
pub fn to_cleared_mut(&mut self, capacity: usize) -> &mut String {
match self {
Text::Static(_) => {
*self = Text::Dynamic(String::with_capacity(capacity).into());
self.to_mut()
}
Text::Dynamic(t) => {
if let Some(t) = Arc::get_mut(t) {
t.clear();
t.reserve(capacity);
}
self.to_mut()
}
}
}
}
impl Encodable for Text {
const FORMAT: Format = <[u8]>::FORMAT;
fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
self.as_bytes().encode(writer)
}
fn encode_header(
&self,
writer: &mut (impl WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
self.as_bytes().encode_header(writer)
}
}
impl Decodable for Text {
fn decode(
&mut self,
reader: &mut (impl crate::codec::ReadsDecodable + ?Sized),
header: Option<crate::codec::DataHeader>,
) -> Result<(), crate::codec::CodecError> {
let header = Self::ensure_header(header, &[0])?;
if header.format.blob_size != 1 || header.format.data_fields != 0 {
return UnexpectedDataFormatSnafu {
expected: Self::FORMAT,
actual: Some(header),
}
.fail();
}
match self {
Text::Static(_) => {
if header.count == 0 {
*self = Text::EMPTY;
} else {
let mut string = String::with_capacity(header.count as usize);
try_decode_string(reader, header, &mut string)?;
*self = Text::Dynamic(string.into());
}
}
Text::Dynamic(text) => {
if header.count == 0 {
if let Some(text) = Arc::get_mut(text) {
text.clear();
} else {
*self = Text::EMPTY;
}
} else {
match Arc::get_mut(text) {
Some(text) => try_decode_string(reader, header, text)?,
None => {
let mut string = String::with_capacity(header.count as usize);
try_decode_string(reader, header, &mut string)?;
*self = Text::Dynamic(string.into());
}
}
}
}
}
Ok(())
}
}
fn try_decode_string(
reader: &mut (impl crate::codec::ReadsDecodable + ?Sized),
header: DataHeader,
string: &mut String,
) -> Result<(), CodecError> {
unsafe {
let bytes = string.as_mut_vec();
bytes.truncate(header.count as usize);
bytes.reserve(header.count as usize);
while bytes.len() < header.count as usize {
bytes.push(0u8);
}
if let Err(e) = reader.read_exact(bytes) {
bytes.clear();
return Err(e);
}
if alloc::str::from_utf8(bytes).is_err() {
bytes.clear();
}
}
Ok(())
}
impl Clone for Text {
fn clone(&self) -> Self {
match self {
Self::Static(text) => Self::Static(text),
Self::Dynamic(text) => Self::Dynamic(text.clone()),
}
}
}
impl Default for Text {
fn default() -> Self {
Self::EMPTY
}
}
impl Deref for Text {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Text::Static(t) => t,
Text::Dynamic(t) => t,
}
}
}
impl From<&str> for Text {
fn from(value: &str) -> Self {
Self::Dynamic(Arc::new(value.to_owned()))
}
}
impl From<String> for Text {
fn from(value: String) -> Self {
Self::Dynamic(value.into())
}
}
impl PartialEq for Text {
fn eq(&self, other: &Self) -> bool {
self.deref() == other.deref()
}
}
impl Eq for Text {}
impl PartialOrd for Text {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Text {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.deref().cmp(other.deref())
}
}
impl Hash for Text {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.deref().hash(state)
}
}
impl PartialEq<str> for Text {
fn eq(&self, other: &str) -> bool {
self.deref() == other
}
}
impl PartialEq<&str> for Text {
fn eq(&self, other: &&str) -> bool {
self.deref() == *other
}
}
impl PartialEq<Text> for &str {
fn eq(&self, other: &Text) -> bool {
*self == other.deref()
}
}
impl PartialEq<Text> for str {
fn eq(&self, other: &Text) -> bool {
self == other.deref()
}
}
impl PartialEq<String> for Text {
fn eq(&self, other: &String) -> bool {
self.deref() == other
}
}
impl PartialEq<Text> for String {
fn eq(&self, other: &Text) -> bool {
self == other.deref()
}
}
impl Display for Text {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<str as Display>::fmt(self, f)
}
}
impl Debug for Text {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<str as Debug>::fmt(self, f)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Text {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Text::Static(text) => text.serialize(serializer),
Text::Dynamic(text) => text.as_str().serialize(serializer),
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Text {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self::Dynamic(String::deserialize(deserializer)?.into()))
}
}
#[cfg(test)]
mod test {
use crate::codec::ReadsDecodable;
use super::*;
#[test]
fn test_text_string_eq() {
let text = Text::from("Hello");
let string = String::from("Hello");
assert_eq!(text, string);
assert_eq!(string, text);
assert_eq!(text, string.as_str());
assert_eq!(string.as_str(), text);
let text = Text::from("World ♥️");
let string = String::from("World ♥️");
assert_eq!(text, string);
assert_eq!(string, text);
assert_eq!(text, string.as_str());
assert_eq!(string.as_str(), text);
let text = Text::from("We Like Text");
let string = String::from("Because Strings are Worse?");
assert_ne!(text, string);
assert_ne!(string, text);
assert_ne!(text, string.as_str());
assert_ne!(string.as_str(), text);
}
#[test]
fn test_text_codec() {
let value = Text::from("Hello, world!");
let mut encoded = vec![];
encoded.write_data(&value).expect("encoded");
let decoded: Text = encoded.as_slice().read_data().expect("decoded");
assert_eq!(value, decoded);
}
}