use crate::core::error::{Error, ErrorKind};
use crate::core::{Chunk, Fragment, Ident};
use crate::syntax::{Boundary, Delimiter, Profile};
use core::marker::PhantomData;
use core::ops::{Bound, RangeBounds};
use std_alloc::collections::TryReserveError;
use std_alloc::string::String;
pub struct FragmentBuf<B, D, P> {
config: PhantomData<(B, D, P)>,
inner: String,
}
impl<B: Boundary, D: Delimiter, P: Profile> FragmentBuf<B, D, P> {
#[must_use]
#[inline]
pub(crate) fn can_push_fragment_char(left: &Fragment<B, D, P>, right: char) -> bool {
match D::APPEND_CLOSED.at_least_fragment() && P::APPEND_CLOSED.at_least_fragment() {
true => true,
false => {
D::is_chunk_delim(right)
|| match left.chars().last() {
None => true,
Some(left) => {
match !P::APPEND_CLOSED.at_least_fragment() && D::is_delim(left) {
true => P::is_chunk_start(right),
false => P::is_chunk_continue(right),
}
}
}
}
}
}
#[must_use]
#[inline]
pub(crate) fn can_join(left: &Fragment<B, D, P>, right: &Fragment<B, D, P>) -> bool {
right
.chars()
.next()
.is_none_or(|c| Self::can_push_fragment_char(left, c))
}
#[inline]
pub fn as_ident(&self) -> Result<&Ident<B, D, P>, Error> {
Ident::from_fragment(self.as_fragment())
}
#[inline]
#[allow(clippy::should_implement_trait)] pub fn from_str(s: &str) -> Result<Self, Error> {
core::str::FromStr::from_str(s)
}
#[inline]
pub fn from_string(s: String) -> Result<Self, Error> {
let _ = Fragment::<B, D, P>::new(&s)?;
Ok(Self::from_string_unchecked(s))
}
#[doc = include_str!("docs/methods/insert_fragment.md")]
#[doc = include_str!("docs/sections/panics.md")]
#[doc = include_str!("docs/sections/errors.md")]
#[inline]
pub fn insert_fragment(
&mut self,
idx: usize,
fragment: &Fragment<B, D, P>,
) -> Result<(), Error> {
let (left, right) = self.split_at(idx);
if !Self::can_join(left, fragment) {
return Err(Error::new(ErrorKind::FailedJoinLeft));
}
if !Self::can_join(fragment, right) {
return Err(Error::new(ErrorKind::FailedJoinRight));
}
self.inner.insert_str(idx, fragment.as_str());
Ok(())
}
#[doc = include_str!("docs/methods/insert_bounded_fragment.md")]
#[doc = include_str!("docs/sections/panics.md")]
#[doc = include_str!("docs/sections/errors.md")]
#[inline]
pub fn insert_bounded_fragment_with(
&mut self,
idx: usize,
fragment: &Fragment<B, D, P>,
delim: D,
) -> Result<(), Error> {
if !B::CAN_FIND_BOUNDARIES {
return self.insert_delimited_fragment_with(idx, fragment, delim);
}
if fragment.is_empty() {
return Ok(());
};
self.insert_fragment(idx, fragment)?;
let left_idx = idx;
let mut right_idx = idx + fragment.len();
let mut left_has_delimiter = self[..left_idx].chars().last().is_none_or(D::is_delim)
|| self[left_idx..].chars().next().is_none_or(D::is_delim);
let mut right_has_delimiter = self[..right_idx].chars().last().is_none_or(D::is_delim)
|| self[right_idx..].chars().next().is_none_or(D::is_delim);
let delim_len = delim.as_char().len_utf8();
for _ in 0..2 {
let left_has_boundary = left_has_delimiter
|| B::has_boundary_at::<P::Segmentation>(self.as_str(), left_idx);
let right_has_boundary = right_has_delimiter
|| B::has_boundary_at::<P::Segmentation>(self.as_str(), right_idx);
if left_has_boundary && right_has_boundary {
break;
}
if !left_has_boundary {
let result = self.insert_delim_with(left_idx, delim);
if result.is_err() {
self.inner.replace_range(left_idx..right_idx, "");
return Err(Error::new(ErrorKind::FailedJoinLeft));
}
left_has_delimiter = true;
right_idx += delim_len;
}
if !right_has_boundary {
let result = self.insert_delim_with(right_idx, delim);
if result.is_err() {
self.inner.replace_range(left_idx..right_idx, "");
return Err(Error::new(ErrorKind::FailedJoinRight));
}
right_has_delimiter = true;
right_idx += delim_len;
}
}
Ok(())
}
#[doc = include_str!("docs/methods/insert_delimited_fragment.md")]
#[doc = include_str!("docs/sections/panics.md")]
#[doc = include_str!("docs/sections/errors.md")]
#[inline]
pub fn insert_delimited_fragment_with(
&mut self,
mut idx: usize,
fragment: &Fragment<B, D, P>,
delim: D,
) -> Result<(), Error> {
if fragment.is_empty() {
return Ok(());
};
let left_has_delim = self[..idx].chars().last().is_none_or(D::is_delim)
|| fragment.chars().next().is_none_or(D::is_delim);
let right_has_delim = self[idx..].chars().next().is_none_or(D::is_delim)
|| fragment.chars().last().is_none_or(D::is_delim);
let left_idx = idx;
let mut right_idx = idx;
let delim_len = delim.as_char().len_utf8();
if !left_has_delim {
self.insert_delim_with(idx, delim)?;
idx += delim_len;
right_idx += delim_len;
}
if !right_has_delim {
let result = self.insert_delim_with(idx, delim);
if result.is_err() {
self.inner.replace_range(left_idx..right_idx, "");
return result;
}
right_idx += delim_len;
}
let result = self.insert_fragment(idx, fragment);
if result.is_err() {
self.inner.replace_range(left_idx..right_idx, "");
}
result
}
#[doc = include_str!("docs/methods/push_delim.md")]
#[doc = include_str!("docs/methods/push_delim.errors.md")]
#[inline]
pub fn push_delim_with(&mut self, delim: D) -> Result<(), Error> {
let delim = delim.as_char();
if !D::APPEND_CLOSED.at_least_fragment()
&& !self.inner.is_empty()
&& !D::is_chunk_delim(delim)
{
return Err(Error::new(ErrorKind::FailedJoinLeft));
}
self.inner.push(delim);
Ok(())
}
#[doc = include_str!("docs/methods/remove.md")]
#[doc = include_str!("docs/sections/panics.md")]
#[inline]
pub fn remove(&mut self, idx: usize) -> Result<(), Error> {
let (left, right) = self.split_at(idx);
let mut chars = right.chars();
let _ = chars.next();
let right = chars.as_fragment();
if !Self::can_join(left, right) {
return Err(Error::new(ErrorKind::FailedRemove).with_byte_offset(idx));
}
self.remove_unchecked(idx);
Ok(())
}
#[doc = include_str!("docs/methods/replace_range.md")]
#[doc = include_str!("docs/sections/panics.md")]
#[inline]
pub fn replace_range_fragment<R>(
&mut self,
range: R,
replace_with: &Fragment<B, D, P>,
) -> Result<(), Error>
where
R: RangeBounds<usize>,
{
let start = match range.start_bound() {
Bound::Included(idx) => *idx,
Bound::Excluded(idx) => *idx + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(idx) => *idx + 1,
Bound::Excluded(idx) => *idx,
Bound::Unbounded => self.len(),
};
let left = &self[..start];
let right = &self[end..];
if !Self::can_join(left, replace_with) {
return Err(Error::new(ErrorKind::FailedReplaceLeft).with_byte_offset(start));
}
if !Self::can_join(replace_with, right) {
return Err(Error::new(ErrorKind::FailedReplaceRight).with_byte_offset(end));
}
self.inner.replace_range(range, replace_with.as_str());
Ok(())
}
#[doc = include_str!("docs/methods/split_off.md")]
#[doc = include_str!("docs/sections/panics.md")]
#[must_use]
#[inline]
pub fn split_off(&mut self, idx: usize) -> FragmentBuf<B, D, P> {
Self::from_string_unchecked(self.inner.split_off(idx))
}
}
impl<B, D, P> FragmentBuf<B, D, P> {
#[must_use]
#[inline]
pub fn from_fragment(fragment: &Fragment<B, D, P>) -> Self {
Self::from_string_unchecked(String::from(fragment.as_str()))
}
#[must_use]
#[inline]
pub(crate) fn from_string_unchecked(orig: String) -> Self {
Self {
config: PhantomData,
inner: orig,
}
}
#[must_use]
#[inline]
pub fn into_string(self) -> String {
self.inner
}
#[must_use]
#[inline]
pub fn leak<'a>(self) -> &'a Fragment<B, D, P> {
Fragment::new_unchecked(self.inner.leak())
}
#[inline]
pub(crate) fn remove_unchecked(&mut self, idx: usize) {
self.inner.remove(idx);
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
config: PhantomData,
inner: String::with_capacity(capacity),
}
}
#[must_use]
#[inline]
pub fn with_overhead(fragment: &Fragment<B, D, P>, additional: usize) -> Self {
let mut inner = String::with_capacity(fragment.len() + additional);
inner.push_str(fragment.as_str());
Self {
config: PhantomData,
inner,
}
}
}
impl_buffer_methods! {
name=FragmentBuf,
}
impl<'a, B, D, P> core::convert::From<&'a Fragment<B, D, P>> for FragmentBuf<B, D, P> {
#[inline]
fn from(orig: &'a Fragment<B, D, P>) -> Self {
Self::from_fragment(orig)
}
}
impl<B, D, P> Clone for FragmentBuf<B, D, P> {
#[inline]
fn clone(&self) -> Self {
Self {
config: PhantomData,
inner: self.inner.clone(),
}
}
}
impl<B, D, P> std_alloc::borrow::Borrow<Fragment<B, D, P>> for FragmentBuf<B, D, P> {
#[inline]
fn borrow(&self) -> &Fragment<B, D, P> {
self.as_fragment()
}
}
impl<B, D, P> std_alloc::borrow::ToOwned for Fragment<B, D, P> {
type Owned = FragmentBuf<B, D, P>;
#[inline]
fn to_owned(&self) -> Self::Owned {
self.to_fragment_buf()
}
}
impl<B: Boundary, D: Delimiter, P: Profile> core::str::FromStr for FragmentBuf<B, D, P> {
type Err = Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from_fragment(Fragment::new(s)?))
}
}
impl_buffer_traits! {
name=FragmentBuf,
}
impl_typed_slice_traits! {
name=FragmentBuf,
index_target=Fragment,
}
impl_typed_slice_cmp! {
name=FragmentBuf,
against=Chunk,
}
impl_typed_slice_cmp! {
name=FragmentBuf,
against=Fragment,
}
impl_typed_slice_cmp! {
name=FragmentBuf,
against=Ident,
}