use core::fmt::Write;
use crate::{AllowEmpty, GString, GStringError, Validator};
impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
GString<V, MIN, MAX, ASCII_ONLY>
{
pub fn push(&mut self, ch: char) -> Result<(), GStringError<V::Err>> {
let mut buf = [0u8; 4];
let encoded = ch.encode_utf8(&mut buf);
self.push_str(encoded)
}
pub fn push_str(&mut self, s: &str) -> Result<(), GStringError<V::Err>> {
let mut buf = [0u8; MAX];
buf[..self.len].copy_from_slice(&self.buf[..self.len]);
let bytes = s.as_bytes();
let end = self.len + bytes.len();
if end > MAX {
return Err(GStringError::TooLong(MAX));
}
buf[self.len..end].copy_from_slice(bytes);
let candidate = unsafe { core::str::from_utf8_unchecked(&buf[..end]) };
*self = Self::try_new(candidate)?;
Ok(())
}
pub fn insert(&mut self, idx: usize, ch: char) -> Result<(), GStringError<V::Err>> {
let mut buf = [0u8; 4];
let encoded = ch.encode_utf8(&mut buf);
self.insert_str(idx, encoded)
}
pub fn insert_str(&mut self, idx: usize, string: &str) -> Result<(), GStringError<V::Err>> {
if !self.as_str().is_char_boundary(idx) {
return Err(GStringError::Mutation("idx is not a char boundary"));
}
let insert_bytes = string.as_bytes();
let insert_len = insert_bytes.len();
let new_len = self.len + insert_len;
if new_len > MAX {
return Err(GStringError::TooLong(MAX));
}
let mut buf = [0u8; MAX];
buf[..idx].copy_from_slice(&self.buf[..idx]);
buf[idx..idx + insert_len].copy_from_slice(insert_bytes);
let tail_len = self.len - idx;
buf[idx + insert_len..idx + insert_len + tail_len]
.copy_from_slice(&self.buf[idx..self.len]);
let candidate = unsafe { core::str::from_utf8_unchecked(&buf[..new_len]) };
*self = Self::try_new(candidate)?;
Ok(())
}
pub fn pop(&mut self) -> Result<Option<char>, GStringError<V::Err>> {
if self.is_empty() {
return Ok(None);
}
let s = self.as_str();
let ch = s.chars().next_back();
let ch = if let Some(ch) = ch {
ch
} else {
return Ok(None);
};
let new_len = self.len - ch.len_utf8();
let candidate = unsafe { core::str::from_utf8_unchecked(&self.buf[..new_len]) };
match Self::try_new(candidate) {
Ok(new) => {
*self = new;
Ok(Some(ch))
}
Err(err) => Err(err),
}
}
pub fn remove(&mut self, idx: usize) -> Result<char, GStringError<V::Err>> {
if !self.as_str().is_char_boundary(idx) {
return Err(GStringError::Mutation("idx is not a char boundary"));
}
let s = self.as_str();
let ch = s[idx..].chars().next().ok_or(GStringError::Mutation(
"cannot remove char from empty index",
))?;
let ch_len = ch.len_utf8();
let new_len = self.len - ch_len;
let mut buf = [0u8; MAX];
buf[..idx].copy_from_slice(&self.buf[..idx]);
buf[idx..new_len].copy_from_slice(&self.buf[idx + ch_len..self.len]);
let candidate = unsafe { core::str::from_utf8_unchecked(&buf[..new_len]) };
*self = Self::try_new(candidate)?;
Ok(ch)
}
pub fn truncate(&mut self, new_len: usize) -> Result<(), GStringError<V::Err>> {
if new_len >= self.len {
return Ok(());
}
if !self.as_str().is_char_boundary(new_len) {
return Err(GStringError::Mutation("new_len is not a char boundary"));
}
let candidate = unsafe { core::str::from_utf8_unchecked(&self.buf[..new_len]) };
*self = Self::try_new(candidate)?;
Ok(())
}
#[cfg(feature = "alloc")]
pub fn replace(&mut self, from: &str, to: &str) -> Result<(), GStringError<V::Err>> {
let replaced = self.as_str().replace(from, to);
*self = Self::try_new(&replaced)?;
Ok(())
}
pub fn replace_range<R>(
&mut self,
range: R,
replace_with: &str,
) -> Result<(), GStringError<V::Err>>
where
R: core::ops::RangeBounds<usize>,
{
use core::ops::Bound;
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n
.checked_add(1)
.ok_or(GStringError::Mutation("range overflow"))?,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n
.checked_add(1)
.ok_or(GStringError::Mutation("range overflow"))?,
Bound::Excluded(&n) => n,
Bound::Unbounded => self.len,
};
let s = self.as_str();
if !s.is_char_boundary(start) {
return Err(GStringError::Mutation("start range is not within boundary"));
}
if !s.is_char_boundary(end) {
return Err(GStringError::Mutation("end range is not within boundary"));
}
if start > end {
return Err(GStringError::Mutation(
"start range cannot be bigger than end",
));
}
let mut buf = [0u8; MAX];
let before = &self.buf[..start];
let middle = replace_with.as_bytes();
let after = &self.buf[end..self.len];
let new_len = before
.len()
.checked_add(middle.len())
.and_then(|n| n.checked_add(after.len()))
.ok_or(GStringError::TooLong(MAX))?;
if new_len > MAX {
return Err(GStringError::TooLong(MAX));
}
buf[..before.len()].copy_from_slice(before);
buf[before.len()..before.len() + middle.len()].copy_from_slice(middle);
buf[before.len() + middle.len()..new_len].copy_from_slice(after);
let candidate = unsafe { core::str::from_utf8_unchecked(&buf[..new_len]) };
*self = Self::try_new(candidate)?;
Ok(())
}
}
impl<V: Validator + AllowEmpty, const MAX: usize, const ASCII_ONLY: bool>
GString<V, 0, MAX, ASCII_ONLY>
{
#[inline]
pub fn clear(&mut self) {
*self = Self::default()
}
}
impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
GString<V, MIN, MAX, ASCII_ONLY>
{
pub fn try_extend<I, S>(&mut self, iter: I) -> Result<(), GStringError<V::Err>>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut tmp = self.clone();
for s in iter {
tmp.push_str(s.as_ref())?;
}
*self = tmp;
Ok(())
}
pub fn try_extend_chars<I>(&mut self, iter: I) -> Result<(), GStringError<V::Err>>
where
I: IntoIterator<Item = char>,
{
let mut tmp = self.clone();
for ch in iter {
tmp.push(ch)?;
}
*self = tmp;
Ok(())
}
}
impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Write
for GString<V, MIN, MAX, ASCII_ONLY>
{
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.push_str(s).map_err(|_| core::fmt::Error)
}
fn write_char(&mut self, c: char) -> core::fmt::Result {
self.push(c).map_err(|_| core::fmt::Error)
}
}