use alloc::string::String;
use core::ops::{Deref, DerefMut};
use redoubt_zero::{FastZeroizable, RedoubtZero, ZeroizeOnDropSentinel};
#[derive(RedoubtZero)]
pub struct RedoubtString {
inner: String,
__sentinel: ZeroizeOnDropSentinel,
}
#[cfg(any(test, feature = "test-utils"))]
impl PartialEq for RedoubtString {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
#[cfg(any(test, feature = "test-utils"))]
impl Eq for RedoubtString {}
impl core::fmt::Debug for RedoubtString {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RedoubtString")
.field("data", &"REDACTED")
.field("len", &self.len())
.field("capacity", &self.capacity())
.finish()
}
}
impl RedoubtString {
pub fn new() -> Self {
Self {
inner: String::new(),
__sentinel: ZeroizeOnDropSentinel::default(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: String::with_capacity(capacity),
__sentinel: ZeroizeOnDropSentinel::default(),
}
}
pub fn from_mut_string(src: &mut String) -> Self {
let mut s = Self::new();
s.extend_from_mut_string(src);
s
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(src: &str) -> Self {
let mut s = Self::new();
s.extend_from_str(src);
s
}
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[cold]
#[inline(never)]
fn grow_to(&mut self, min_capacity: usize) {
let new_capacity = min_capacity.next_power_of_two();
let mut tmp = self.inner.clone();
self.inner.fast_zeroize();
self.inner.clear();
self.inner.shrink_to_fit();
self.inner.reserve_exact(new_capacity);
self.extend_from_mut_string(&mut tmp);
}
#[inline(always)]
fn maybe_grow_to(&mut self, min_capacity: usize) {
if self.capacity() >= min_capacity {
return;
}
self.grow_to(min_capacity);
}
pub fn extend_from_mut_string(&mut self, src: &mut String) {
self.maybe_grow_to(self.len() + src.len());
self.inner.push_str(src);
src.fast_zeroize();
src.clear();
}
pub fn replace_from_mut_string(&mut self, src: &mut String) {
self.clear();
self.extend_from_mut_string(src);
}
pub fn extend_from_str(&mut self, src: &str) {
self.maybe_grow_to(self.len() + src.len());
self.inner.push_str(src);
}
pub fn clear(&mut self) {
self.inner.fast_zeroize();
self.inner.clear();
}
pub fn as_str(&self) -> &str {
&self.inner
}
pub fn as_mut_str(&mut self) -> &mut str {
&mut self.inner
}
pub fn as_string(&self) -> &String {
&self.inner
}
pub fn as_mut_string(&mut self) -> &mut String {
&mut self.inner
}
}
impl Default for RedoubtString {
fn default() -> Self {
Self::new()
}
}
impl Deref for RedoubtString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for RedoubtString {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}