Skip to main content

typed_ident/alloc/
fragment_buf.rs

1// =============================================================================
2// USES
3// =============================================================================
4
5// -----------------------------------------------------------------------------
6use crate::core::error::{Error, ErrorKind};
7use crate::core::{Chunk, Fragment, Ident};
8use crate::syntax::{Boundary, Delimiter, Profile};
9use core::marker::PhantomData;
10use core::ops::{Bound, RangeBounds};
11use std_alloc::collections::TryReserveError;
12use std_alloc::string::String;
13
14// =============================================================================
15// TYPES
16// =============================================================================
17
18/// A dynamic, growable fragment.
19///
20/// This allows you to build a fragment dynamically, instead of having to get a
21/// fragment from an identifier slice.
22pub struct FragmentBuf<B, D, P> {
23    config: PhantomData<(B, D, P)>,
24    inner: String,
25}
26
27// =============================================================================
28// IMPLS
29// =============================================================================
30
31// -----------------------------------------------------------------------------
32impl<B: Boundary, D: Delimiter, P: Profile> FragmentBuf<B, D, P> {
33    #[must_use]
34    #[inline]
35    pub(crate) fn can_push_fragment_char(left: &Fragment<B, D, P>, right: char) -> bool {
36        // Append-closed means that taking any fragment and appending it on the
37        // end of any other fragment, is a valid activity. Here, we're looking
38        // at a single `char`, verified to be a valid part of a fragment.
39        match D::APPEND_CLOSED.at_least_fragment() && P::APPEND_CLOSED.at_least_fragment() {
40            true => true,
41            false => {
42                // If the first character to the right of the join is a chunk
43                // delimiter, then it's always safe to join.
44                //
45                // Otherwise, we need to inspect the last character to the left
46                // of the join - if it's a delimiter we need a chunk start, and
47                // if it's not a delimiter we're in a chunk, so we need a chunk
48                // continue character next.
49                D::is_chunk_delim(right)
50                    || match left.chars().last() {
51                        None => true,
52                        // If the profile is append-closed, then we don't need
53                        // to check if it's a delimiter, because any character
54                        // from chunk-continue is valid after a delim or char.
55                        Some(left) => {
56                            match !P::APPEND_CLOSED.at_least_fragment() && D::is_delim(left) {
57                                true => P::is_chunk_start(right),
58                                false => P::is_chunk_continue(right),
59                            }
60                        }
61                    }
62            }
63        }
64    }
65
66    #[must_use]
67    #[inline]
68    pub(crate) fn can_join(left: &Fragment<B, D, P>, right: &Fragment<B, D, P>) -> bool {
69        right
70            .chars()
71            .next()
72            .is_none_or(|c| Self::can_push_fragment_char(left, c))
73    }
74
75    /// Attempts to represent the current fragment buffer as an [`Ident`].
76    ///
77    /// This may fail - a fragment isn't obviously a valid identifier, plus the
78    /// fragment could be empty (which is never a valid identifier).
79    ///
80    /// # Examples
81    ///
82    /// Basic Usage:
83    ///
84    /// ```
85    /// # use typed_ident::*;
86    /// # use presets::unicode::hybrid::HybridFragmentBuf as UnicodeFragmentBuf;
87    /// let mut buffer = UnicodeFragmentBuf::new();
88    /// assert!(buffer.as_ident().is_err()); // Empty
89    /// buffer.push('2')?;
90    /// assert!(buffer.as_ident().is_err()); // Invalid start character
91    ///
92    /// buffer.clear();
93    /// buffer.push('a')?;
94    /// assert!(buffer.as_ident().is_ok()); // Valid!
95    /// # Ok::<(), Error>(())
96    /// ```
97    #[inline]
98    pub fn as_ident(&self) -> Result<&Ident<B, D, P>, Error> {
99        Ident::from_fragment(self.as_fragment())
100    }
101
102    /// Constructs a fragment buffer, initializing the contents to a provided
103    /// string slice (attempting first to convert the string slice to a
104    /// fragment).
105    ///
106    /// This is equivalent to `FragmentBuf::from_fragment(Fragment::new(s)?)`.
107    ///
108    /// # Examples
109    ///
110    /// Basic Usage:
111    ///
112    /// ```
113    /// # use typed_ident::*;
114    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
115    /// assert!(UpperCamelFragmentBuf::from_str("").is_ok());
116    /// assert!(UpperCamelFragmentBuf::from_str("ValidUpperCamel").is_ok());
117    /// assert!(UpperCamelFragmentBuf::from_str("continuingUpperCamel").is_ok());
118    /// assert!(UpperCamelFragmentBuf::from_str("not_validUpperCamel").is_err());
119    /// # Ok::<(), Error>(())
120    /// ```
121    #[inline]
122    #[allow(clippy::should_implement_trait)] // It *does* implement the trait.
123    pub fn from_str(s: &str) -> Result<Self, Error> {
124        core::str::FromStr::from_str(s)
125    }
126
127    /// Constructs a fragment buffer, initializing the contents to a provided
128    /// buffered string (checking first that the string is a valid fragment).
129    ///
130    /// This is similar to [`from_str`], except that it will not allocate a
131    /// separate string. It will use the provided string, if it's valid.
132    ///
133    /// [`from_str`]: Self::from_str
134    ///
135    /// # Examples
136    ///
137    /// Basic Usage:
138    ///
139    /// ```
140    /// # use typed_ident::*;
141    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
142    /// assert!(UpperCamelFragmentBuf::from_string(String::from("")).is_ok());
143    /// assert!(UpperCamelFragmentBuf::from_string(String::from("ValidUpperCamel")).is_ok());
144    /// assert!(UpperCamelFragmentBuf::from_string(String::from("continuingUpperCamel")).is_ok());
145    /// assert!(UpperCamelFragmentBuf::from_string(String::from("not_validUpperCamel")).is_err());
146    /// # Ok::<(), Error>(())
147    #[inline]
148    pub fn from_string(s: String) -> Result<Self, Error> {
149        let _ = Fragment::<B, D, P>::new(&s)?;
150        Ok(Self::from_string_unchecked(s))
151    }
152
153    #[doc = include_str!("docs/methods/insert_fragment.md")]
154    #[doc = include_str!("docs/sections/panics.md")]
155    #[doc = include_str!("docs/sections/errors.md")]
156    ///
157    /// # Examples
158    ///
159    /// Basic Usage:
160    ///
161    /// ```
162    /// # use typed_ident::*;
163    /// # use presets::unicode::upper_camel::UpperCamelFragment;
164    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
165    /// let mut buffer = UpperCamelFragmentBuf::from_str("UpperCamel")?;
166    ///
167    /// // Inserting at the beginning is ~prepend.
168    /// let mut example = buffer.clone();
169    /// assert!(example.insert_fragment(
170    ///     0,
171    ///     UpperCamelFragment::new("HAT")?,
172    /// ).is_ok());
173    /// assert_eq!(example, "HATUpperCamel");
174    ///
175    /// // Inserting at the end is ~append.
176    /// let mut example = buffer.clone();
177    /// assert!(example.insert_fragment(
178    ///     example.len(),
179    ///     UpperCamelFragment::new("lower")?,
180    /// ).is_ok());
181    /// assert!(example.insert_fragment(
182    ///     example.len(),
183    ///     UpperCamelFragment::new("Camel")?,
184    /// ).is_ok());
185    /// assert_eq!(example, "UpperCamellowerCamel");
186    ///
187    /// // Inserting in the middle can be tricky, as your insertions
188    /// // may invalidate the buffer's invariants in surprising ways.
189    /// let mut example = buffer.clone();
190    /// assert!(example.insert_fragment(
191    ///     2,
192    ///     UpperCamelFragment::new("HAT_")?,
193    /// ).is_err()); // "UpHAT_perCamel" != UpperCamel casing
194    /// assert!(example.insert_fragment(
195    ///     5,
196    ///     UpperCamelFragment::new("HAT")?,
197    /// ).is_ok());
198    /// assert_eq!(example, "UpperHATCamel");
199    /// # Ok::<(), Error>(())
200    /// ```
201    #[inline]
202    pub fn insert_fragment(
203        &mut self,
204        idx: usize,
205        fragment: &Fragment<B, D, P>,
206    ) -> Result<(), Error> {
207        let (left, right) = self.split_at(idx);
208        if !Self::can_join(left, fragment) {
209            return Err(Error::new(ErrorKind::FailedJoinLeft));
210        }
211        if !Self::can_join(fragment, right) {
212            return Err(Error::new(ErrorKind::FailedJoinRight));
213        }
214        self.inner.insert_str(idx, fragment.as_str());
215        Ok(())
216    }
217
218    #[doc = include_str!("docs/methods/insert_bounded_fragment.md")]
219    #[doc = include_str!("docs/sections/panics.md")]
220    #[doc = include_str!("docs/sections/errors.md")]
221    ///
222    /// # Examples
223    ///
224    /// Basic Usage:
225    ///
226    /// ```
227    /// # use typed_ident::*;
228    /// # use typed_ident::syntax::delimiter::*;
229    /// # use typed_ident::presets::unicode::upper_camel::*;
230    /// let mut buffer = UpperCamelFragmentBuf::from_str("UpperCamel")?;
231    ///
232    /// // Inserting at the beginning is ~prepend.
233    /// let mut example = buffer.clone();
234    /// assert!(example.insert_bounded_fragment_with(
235    ///     0,
236    ///     UpperCamelFragment::new("HAT")?,
237    ///     LowLine,
238    /// ).is_ok()); // Bounded because of `HAT` rules.
239    /// assert!(example.insert_bounded_fragment_with(
240    ///     0,
241    ///     UpperCamelFragment::new("HAT")?,
242    ///     LowLine,
243    /// ).is_ok()); // But another would not be.
244    /// assert_eq!(example, "HAT_HATUpperCamel");
245    ///
246    /// // Inserting at the end is ~append.
247    /// let mut example = buffer.clone();
248    /// assert!(example.insert_bounded_fragment_with(
249    ///     example.len(),
250    ///     UpperCamelFragment::new("lower")?,
251    ///     LowLine,
252    /// ).is_err()); // "UpperCamel_lower" != UpperCamel casing
253    /// assert!(example.insert_bounded_fragment_with(
254    ///     example.len(),
255    ///     UpperCamelFragment::new("Camel")?,
256    ///     LowLine,
257    /// ).is_ok()); // Because of `CAMEL` boundary.
258    /// assert_eq!(example, "UpperCamelCamel");
259    ///
260    /// // Inserting in the middle can be tricky, as your insertions
261    /// // may invalidate the buffer's invariants in surprising ways.
262    /// let mut example = buffer.clone();
263    /// assert!(example.insert_bounded_fragment_with(
264    ///     2,
265    ///     UpperCamelFragment::new("HAT")?,
266    ///     LowLine,
267    /// ).is_err()); // "UpHAT_perCamel" != UpperCamel casing
268    /// assert!(example.insert_bounded_fragment_with(
269    ///     5,
270    ///     UpperCamelFragment::new("HAT")?,
271    ///     LowLine,
272    /// ).is_ok()); // Surprisingly a `CAMEL` & `HAT` boundary.
273    /// assert_eq!(example, "UpperHATCamel");
274    /// # Ok::<(), Error>(())
275    /// ```
276    #[inline]
277    pub fn insert_bounded_fragment_with(
278        &mut self,
279        idx: usize,
280        fragment: &Fragment<B, D, P>,
281        delim: D,
282    ) -> Result<(), Error> {
283        // Edge-Case: The boundary definition requires a delimiter.
284        //
285        // In this case, we know we can't form a natural chunk boundary. So we
286        // should just redirect the call to `insert_delimited_fragment_with`.
287        //
288        // We hope that the compiler will then drop the rest of this function.
289        if !B::CAN_FIND_BOUNDARIES {
290            return self.insert_delimited_fragment_with(idx, fragment, delim);
291        }
292
293        // Edge-Case: There's no fragment data.
294        //
295        // The invariants of this function is that chunk boundaries are
296        // maintained. An empty fragment cannot disrupt any chunk boundaries.
297        //
298        // Just return.
299        if fragment.is_empty() {
300            return Ok(());
301        };
302
303        // Since we know this *doesn't* require a delimiter, we can just try to
304        // insert the fragment and see if boundaries are preserved.
305        //
306        // This obviously can fail, but interestingly, because we defined the
307        // profile to have chunk_continue characters be a superset of chunk and
308        // ident start characters, it can only fail in ways that we would not
309        // anyways be able to remedy by simply adding a delimiter (which, for
310        // this function, is our only possible remediation).
311        //
312        // So, thankfully, we can start with an insert, then check the sides.
313        self.insert_fragment(idx, fragment)?;
314
315        // Let's keep track of the complete inserted range of characters.
316        // This way, if we need to undo, we can by simply removing the range.
317        let left_idx = idx;
318        let mut right_idx = idx + fragment.len();
319
320        // Identify if there was already a delimiter on either end in the input.
321        // This will help us determine if we need to perform boundary checks.
322        let mut left_has_delimiter = self[..left_idx].chars().last().is_none_or(D::is_delim)
323            || self[left_idx..].chars().next().is_none_or(D::is_delim);
324        let mut right_has_delimiter = self[..right_idx].chars().last().is_none_or(D::is_delim)
325            || self[right_idx..].chars().next().is_none_or(D::is_delim);
326
327        // We need to loop at most twice - fixing one side may break the other.
328        // (e.g. UpperCamel -> UUpperCamel -> UU_pperCamel -> U_U_pperCamel)
329        let delim_len = delim.as_char().len_utf8();
330        for _ in 0..2 {
331            // Check if the left and right sides require a delimiter.
332            let left_has_boundary = left_has_delimiter
333                || B::has_boundary_at::<P::Segmentation>(self.as_str(), left_idx);
334            let right_has_boundary = right_has_delimiter
335                || B::has_boundary_at::<P::Segmentation>(self.as_str(), right_idx);
336            if left_has_boundary && right_has_boundary {
337                break;
338            }
339
340            // Try to fix-up the left-hand side first - on failure, undo the insert.
341            if !left_has_boundary {
342                let result = self.insert_delim_with(left_idx, delim);
343                if result.is_err() {
344                    // Don't need to check it again, if it was valid before, it's
345                    // still valid after we "undo".
346                    self.inner.replace_range(left_idx..right_idx, "");
347                    return Err(Error::new(ErrorKind::FailedJoinLeft));
348                }
349                left_has_delimiter = true;
350                right_idx += delim_len;
351            }
352
353            // Try to fix-up the right-hand side next - on failure, undo the insert.
354            if !right_has_boundary {
355                let result = self.insert_delim_with(right_idx, delim);
356                if result.is_err() {
357                    // Don't need to check it again, if it was valid before, it's
358                    // still valid after we "undo".
359                    self.inner.replace_range(left_idx..right_idx, "");
360                    return Err(Error::new(ErrorKind::FailedJoinRight));
361                }
362                right_has_delimiter = true;
363                right_idx += delim_len;
364            }
365        }
366
367        Ok(())
368    }
369
370    #[doc = include_str!("docs/methods/insert_delimited_fragment.md")]
371    #[doc = include_str!("docs/sections/panics.md")]
372    #[doc = include_str!("docs/sections/errors.md")]
373    ///
374    /// # Examples
375    ///
376    /// Basic Usage:
377    ///
378    /// ```
379    /// # use typed_ident::*;
380    /// # use typed_ident::syntax::delimiter::*;
381    /// # use presets::unicode::upper_camel::*;
382    /// let mut buffer = UpperCamelFragmentBuf::from_str("UpperCamel")?;
383    ///
384    /// // Inserting at the beginning is ~prepend.
385    /// let mut example = buffer.clone();
386    /// assert!(example.insert_delimited_fragment_with(
387    ///     0,
388    ///     UpperCamelFragment::new("HAT")?,
389    ///     LowLine,
390    /// ).is_ok());
391    /// assert_eq!(example, "HAT_UpperCamel");
392    ///
393    /// // Inserting at the end is ~append.
394    /// let mut example = buffer.clone();
395    /// assert!(example.insert_delimited_fragment_with(
396    ///     example.len(),
397    ///     UpperCamelFragment::new("lower")?,
398    ///     LowLine,
399    /// ).is_err()); // "UpperCamel_lower" != UpperCamel casing
400    /// assert!(example.insert_delimited_fragment_with(
401    ///     example.len(),
402    ///     UpperCamelFragment::new("Camel")?,
403    ///     LowLine,
404    /// ).is_ok());
405    /// assert_eq!(example, "UpperCamel_Camel");
406    ///
407    /// // Inserting in the middle can be tricky, as your insertions
408    /// // may invalidate the buffer's invariants in surprising ways.
409    /// let mut example = buffer.clone();
410    /// assert!(example.insert_delimited_fragment_with(
411    ///     2,
412    ///     UpperCamelFragment::new("HAT")?,
413    ///     LowLine,
414    /// ).is_err()); // "Up_HAT_perCamel" != UpperCamel casing
415    /// assert!(example.insert_delimited_fragment_with(
416    ///     5,
417    ///     UpperCamelFragment::new("HAT")?,
418    ///     LowLine,
419    /// ).is_ok());
420    /// assert_eq!(example, "Upper_HAT_Camel");
421    /// # Ok::<(), Error>(())
422    /// ```
423    #[inline]
424    pub fn insert_delimited_fragment_with(
425        &mut self,
426        mut idx: usize,
427        fragment: &Fragment<B, D, P>,
428        delim: D,
429    ) -> Result<(), Error> {
430        // Edge-Case: There's no fragment data.
431        //
432        // The invariants of this function is that chunk boundaries are
433        // maintained. An empty fragment cannot disrupt any chunk boundaries.
434        //
435        // Just return.
436        if fragment.is_empty() {
437            return Ok(());
438        };
439
440        // Since we know this *requires* delimiters, we can check the inserting
441        // fragment to see if we need delimiters on either end.
442        let left_has_delim = self[..idx].chars().last().is_none_or(D::is_delim)
443            || fragment.chars().next().is_none_or(D::is_delim);
444        let right_has_delim = self[idx..].chars().next().is_none_or(D::is_delim)
445            || fragment.chars().last().is_none_or(D::is_delim);
446
447        // Let's keep track of the complete inserted range of characters.
448        // This way, if we need to undo, we can by simply removing the range.
449        let left_idx = idx;
450        let mut right_idx = idx;
451        let delim_len = delim.as_char().len_utf8();
452
453        // If either side will be missing a delim after insert, we will need to
454        // insert some delimiters.
455        if !left_has_delim {
456            self.insert_delim_with(idx, delim)?;
457            idx += delim_len;
458            right_idx += delim_len;
459        }
460        if !right_has_delim {
461            let result = self.insert_delim_with(idx, delim);
462            if result.is_err() {
463                // Don't need to check it again, if it was valid before, it's
464                // still valid after we "undo".
465                self.inner.replace_range(left_idx..right_idx, "");
466                return result;
467            }
468            right_idx += delim_len;
469        }
470
471        // Finally, we can simply insert the fragment, and it will either work
472        // or not (nothing we can do but return an error if not).
473        let result = self.insert_fragment(idx, fragment);
474        if result.is_err() {
475            // Don't need to check it again, if it was valid before, it's
476            // still valid after we "undo".
477            self.inner.replace_range(left_idx..right_idx, "");
478        }
479        result
480    }
481
482    #[doc = include_str!("docs/methods/push_delim.md")]
483    #[doc = include_str!("docs/methods/push_delim.errors.md")]
484    ///
485    /// # Examples
486    ///
487    /// Basic Usage:
488    ///
489    /// ```
490    /// # use typed_ident::*;
491    /// # use syntax::delimiter::LowLine;
492    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
493    /// let mut buffer = UpperCamelFragmentBuf::new();
494    ///
495    /// // For all preset and provided delimiters, you can push them anywhere in
496    /// // an identifier. Unless you have a custom delimiter, it's always safe to push.
497    /// assert!(buffer.push_delim_with(LowLine).is_ok());
498    /// # Ok::<(), Error>(())
499    /// ```
500    ///
501    /// Example Failure:
502    ///
503    /// ```
504    /// # use typed_ident::*;
505    /// # use typed_ident::syntax::*;
506    /// # use syntax::delimiter::LowLine;
507    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
508    /// #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
509    /// struct DollarStart;
510    ///
511    /// impl Delimiter for DollarStart {
512    ///     fn as_char(&self) -> char {
513    ///         '$'
514    ///     }
515    ///     fn from_ident_start(c: char) -> Option<Self> {
516    ///         match c {
517    ///             '$' => Some(Self),
518    ///             _ => None,
519    ///         }
520    ///     }
521    ///     fn from_chunk_delim(c: char) -> Option<Self> {
522    ///         None
523    ///     }
524    /// }
525    ///
526    /// type DollarStartFragmentBuf = FragmentBuf<
527    ///     boundary::Standard,
528    ///     DollarStart,
529    ///     profile::Unicode,
530    /// >;
531    ///
532    /// let mut buffer = DollarStartFragmentBuf::new();
533    ///
534    /// // Okay to push one `$` in, because it may be the start fragment.
535    /// assert!(buffer.push_delim_with(DollarStart).is_ok());
536    ///
537    /// // But you definitely cannot push another in - that's invalid.
538    /// assert!(buffer.push_delim_with(DollarStart).is_err());
539    /// # Ok::<(), Error>(())
540    /// ```
541    #[inline]
542    pub fn push_delim_with(&mut self, delim: D) -> Result<(), Error> {
543        let delim = delim.as_char();
544        if !D::APPEND_CLOSED.at_least_fragment()
545            && !self.inner.is_empty()
546            && !D::is_chunk_delim(delim)
547        {
548            return Err(Error::new(ErrorKind::FailedJoinLeft));
549        }
550        self.inner.push(delim);
551        Ok(())
552    }
553
554    #[doc = include_str!("docs/methods/remove.md")]
555    #[doc = include_str!("docs/sections/panics.md")]
556    ///
557    /// # Errors
558    ///
559    /// If the removal of the character at the provided index would lead to an
560    /// invalid buffer, then the character will not be remove and instead the
561    /// error `FailedRemove` will be returned.
562    ///
563    /// # Examples
564    ///
565    /// Basic Usage:
566    ///
567    /// ```
568    /// # use typed_ident::*;
569    /// # use typed_ident::syntax::delimiter::*;
570    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
571    /// let mut buffer = UpperCamelFragmentBuf::from_str("Upper_Camel")?;
572    ///
573    /// // This would be valid, because it might be a continuation fragment.
574    /// assert!(buffer.remove(0).is_ok());
575    /// assert_eq!(buffer, "pper_Camel");
576    ///
577    /// // However, attempting to remove `C` would fail for `UpperCamel`.
578    /// assert!(buffer.remove(5).is_err());
579    /// assert_eq!(buffer, "pper_Camel");
580    /// # Ok::<(), Error>(())
581    /// ```
582    #[inline]
583    pub fn remove(&mut self, idx: usize) -> Result<(), Error> {
584        let (left, right) = self.split_at(idx);
585        let mut chars = right.chars();
586        let _ = chars.next();
587        let right = chars.as_fragment();
588        if !Self::can_join(left, right) {
589            return Err(Error::new(ErrorKind::FailedRemove).with_byte_offset(idx));
590        }
591        self.remove_unchecked(idx);
592        Ok(())
593    }
594
595    #[doc = include_str!("docs/methods/replace_range.md")]
596    #[doc = include_str!("docs/sections/panics.md")]
597    ///
598    /// # Errors
599    ///
600    /// Returns `Err` if the fragment formed from the combination of `self` and
601    /// `to` is invalid at any replacement index. If invalid, an [`Error`] is
602    /// returned with the [`error_kind`] set to `FailedReplaceLeft` or
603    /// `FailedReplaceRight` (if the replacement succeeded, but the
604    /// remainder could not be appended).
605    ///
606    /// The value [`byte_offset`] *WILL* be set from this function, and it will
607    /// be set to the index that caused the failure from the original fragment
608    /// (`self`).
609    ///
610    /// [`Error`]: crate::Error
611    /// [`error_kind`]: crate::Error::error_kind
612    /// [`byte_offset`]: crate::Error::byte_offset
613    ///
614    /// # Examples
615    ///
616    /// Basic Usage:
617    ///
618    /// ```
619    /// # use typed_ident::*;
620    /// # use typed_ident::syntax::delimiter::*;
621    /// # use presets::unicode::upper_camel::*;
622    /// let buffer = UpperCamelFragmentBuf::from_str("Upper_Camel")?;
623    ///
624    /// // Examples replacing various ranges.
625    /// let replacement = UpperCamelFragment::new("R")?;
626    /// let mut example = buffer.clone();
627    /// assert!(example.replace_range_fragment(4..7, replacement).is_ok());
628    /// assert_eq!(example, "UppeRamel");
629    ///
630    /// let mut example = buffer.clone();
631    /// assert!(example.replace_range_fragment(4..=7, replacement).is_ok());
632    /// assert_eq!(example, "UppeRmel");
633    ///
634    /// let mut example = buffer.clone();
635    /// assert!(example.replace_range_fragment(..7, replacement).is_ok());
636    /// assert_eq!(example, "Ramel");
637    ///
638    /// let mut example = buffer.clone();
639    /// assert!(example.replace_range_fragment(..=7, replacement).is_ok());
640    /// assert_eq!(example, "Rmel");
641    ///
642    /// let mut example = buffer.clone();
643    /// assert!(example.replace_range_fragment(4.., replacement).is_ok());
644    /// assert_eq!(example, "UppeR");
645    /// # Ok::<(), Error>(())
646    /// ```
647    #[inline]
648    pub fn replace_range_fragment<R>(
649        &mut self,
650        range: R,
651        replace_with: &Fragment<B, D, P>,
652    ) -> Result<(), Error>
653    where
654        R: RangeBounds<usize>,
655    {
656        let start = match range.start_bound() {
657            Bound::Included(idx) => *idx,
658            Bound::Excluded(idx) => *idx + 1,
659            Bound::Unbounded => 0,
660        };
661        let end = match range.end_bound() {
662            Bound::Included(idx) => *idx + 1,
663            Bound::Excluded(idx) => *idx,
664            Bound::Unbounded => self.len(),
665        };
666        let left = &self[..start];
667        let right = &self[end..];
668        if !Self::can_join(left, replace_with) {
669            return Err(Error::new(ErrorKind::FailedReplaceLeft).with_byte_offset(start));
670        }
671        if !Self::can_join(replace_with, right) {
672            return Err(Error::new(ErrorKind::FailedReplaceRight).with_byte_offset(end));
673        }
674        self.inner.replace_range(range, replace_with.as_str());
675        Ok(())
676    }
677
678    #[doc = include_str!("docs/methods/split_off.md")]
679    #[doc = include_str!("docs/sections/panics.md")]
680    ///
681    /// # Errors
682    ///
683    /// If the replacement of the range provided with the given fragment would
684    /// lead to an invalid buffer, then the range will not be remove and instead
685    /// the error `InvalidReplace` will be returned.
686    ///
687    /// # Examples
688    ///
689    /// Basic Usage:
690    ///
691    /// ```
692    /// # use typed_ident::*;
693    /// # use typed_ident::syntax::delimiter::*;
694    /// # use presets::unicode::upper_camel::*;
695    /// let mut buffer = UpperCamelFragmentBuf::from_str("UpperCamel")?;
696    /// let split = buffer.split_off(5);
697    /// assert_eq!(buffer, "Upper");
698    /// assert_eq!(split, "Camel");
699    /// # Ok::<(), Error>(())
700    /// ```
701    #[must_use]
702    #[inline]
703    pub fn split_off(&mut self, idx: usize) -> FragmentBuf<B, D, P> {
704        Self::from_string_unchecked(self.inner.split_off(idx))
705    }
706}
707
708// -----------------------------------------------------------------------------
709impl<B, D, P> FragmentBuf<B, D, P> {
710    /// Converts a fragment slice into a fragment buffer.
711    ///
712    /// # Examples
713    ///
714    /// ```
715    /// # use typed_ident::*;
716    /// # use presets::unicode::upper_camel::UpperCamelFragment;
717    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
718    /// let fragment = UpperCamelFragment::new("example")?;
719    /// let mut buffer = UpperCamelFragmentBuf::from_fragment(fragment);
720    /// assert_eq!(buffer, "example");
721    /// # Ok::<(), Error>(())
722    /// ```
723    #[must_use]
724    #[inline]
725    pub fn from_fragment(fragment: &Fragment<B, D, P>) -> Self {
726        Self::from_string_unchecked(String::from(fragment.as_str()))
727    }
728
729    /// Converts an allocated string into a fragment buffer, without checking if
730    /// the allocated string is a valid fragment or not.
731    ///
732    /// # Safety
733    ///
734    /// You can only call this if the input string is from a valid [`Fragment`]
735    /// over the same generic parameters, or if you have ensured the string
736    /// *would* have been valid.
737    ///
738    /// Needless to say, this is difficult to know unless you are taking a slice
739    /// of an existing fragment/ident/etc, or if you are testing this at compile
740    /// time.
741    ///
742    /// [`Fragment`]: crate::core::Fragment
743    #[must_use]
744    #[inline]
745    pub(crate) fn from_string_unchecked(orig: String) -> Self {
746        Self {
747            config: PhantomData,
748            inner: orig,
749        }
750    }
751
752    /// Convert the buffer into an owned string.
753    ///
754    /// # Examples
755    ///
756    /// ```
757    /// # use typed_ident::presets::unicode::upper_camel::*;
758    /// let fragment = UpperCamelFragmentBuf::from_str("example")?;
759    /// let string: String = fragment.into_string();
760    /// assert_eq!(string, "example");
761    /// # Ok::<(), typed_ident::Error>(())
762    /// ```
763    #[must_use]
764    #[inline]
765    pub fn into_string(self) -> String {
766        self.inner
767    }
768
769    /// Leaks the fragment so that it lives for the rest of the execution of the
770    /// program.
771    ///
772    /// This is a typed wrapper over the [`String::leak`] method.
773    ///
774    /// # Examples
775    ///
776    /// ```no_run
777    /// # use typed_ident::presets::unicode::upper_camel::*;
778    /// let fragment = UpperCamelFragmentBuf::from_str("example")?;
779    /// let string: &'static UpperCamelFragment = fragment.leak();
780    /// # Ok::<(), typed_ident::Error>(())
781    /// ```
782    #[must_use]
783    #[inline]
784    pub fn leak<'a>(self) -> &'a Fragment<B, D, P> {
785        Fragment::new_unchecked(self.inner.leak())
786    }
787
788    #[inline]
789    pub(crate) fn remove_unchecked(&mut self, idx: usize) {
790        self.inner.remove(idx);
791    }
792
793    /// Constructs an empty fragment buffer with an initial capacity.
794    ///
795    /// This has the same properties as [`String::with_capacity`].
796    ///
797    /// # Examples
798    ///
799    /// ```
800    /// # use typed_ident::presets::unicode::upper_camel::*;
801    /// let buffer = UpperCamelFragmentBuf::with_capacity(10);
802    /// assert!(buffer.capacity() >= 10);
803    /// # Ok::<(), typed_ident::Error>(())
804    /// ```
805    #[inline]
806    pub fn with_capacity(capacity: usize) -> Self {
807        Self {
808            config: PhantomData,
809            inner: String::with_capacity(capacity),
810        }
811    }
812
813    /// Constructs a fragment with enough space to hold the provided fragment,
814    /// as well as `additional` bytes, then initializes the contents of this
815    /// buffer to `fragment`.
816    ///
817    /// # Examples
818    ///
819    /// ```
820    /// # use typed_ident::presets::unicode::upper_camel::*;
821    /// let fragment = UpperCamelFragment::new("example")?;
822    /// let buffer = UpperCamelFragmentBuf::with_overhead(fragment, 20);
823    /// assert!(buffer.capacity() >= 27);
824    /// assert_eq!(buffer, "example");
825    /// # Ok::<(), typed_ident::Error>(())
826    /// ```
827    #[must_use]
828    #[inline]
829    pub fn with_overhead(fragment: &Fragment<B, D, P>, additional: usize) -> Self {
830        let mut inner = String::with_capacity(fragment.len() + additional);
831        inner.push_str(fragment.as_str());
832        Self {
833            config: PhantomData,
834            inner,
835        }
836    }
837}
838
839// -----------------------------------------------------------------------------
840impl_buffer_methods! {
841    name=FragmentBuf,
842}
843
844// =============================================================================
845// TRAIT IMPL
846// =============================================================================
847
848// -----------------------------------------------------------------------------
849impl<'a, B, D, P> core::convert::From<&'a Fragment<B, D, P>> for FragmentBuf<B, D, P> {
850    #[inline]
851    fn from(orig: &'a Fragment<B, D, P>) -> Self {
852        Self::from_fragment(orig)
853    }
854}
855
856// -----------------------------------------------------------------------------
857impl<B, D, P> Clone for FragmentBuf<B, D, P> {
858    #[inline]
859    fn clone(&self) -> Self {
860        Self {
861            config: PhantomData,
862            inner: self.inner.clone(),
863        }
864    }
865}
866
867// -----------------------------------------------------------------------------
868impl<B, D, P> std_alloc::borrow::Borrow<Fragment<B, D, P>> for FragmentBuf<B, D, P> {
869    #[inline]
870    fn borrow(&self) -> &Fragment<B, D, P> {
871        self.as_fragment()
872    }
873}
874
875// -----------------------------------------------------------------------------
876impl<B, D, P> std_alloc::borrow::ToOwned for Fragment<B, D, P> {
877    type Owned = FragmentBuf<B, D, P>;
878    #[inline]
879    fn to_owned(&self) -> Self::Owned {
880        self.to_fragment_buf()
881    }
882}
883
884// -----------------------------------------------------------------------------
885impl<B: Boundary, D: Delimiter, P: Profile> core::str::FromStr for FragmentBuf<B, D, P> {
886    type Err = Error;
887    #[inline]
888    fn from_str(s: &str) -> Result<Self, Self::Err> {
889        Ok(Self::from_fragment(Fragment::new(s)?))
890    }
891}
892
893// -----------------------------------------------------------------------------
894impl_buffer_traits! {
895    name=FragmentBuf,
896}
897
898// -----------------------------------------------------------------------------
899impl_typed_slice_traits! {
900    name=FragmentBuf,
901    index_target=Fragment,
902}
903
904// -----------------------------------------------------------------------------
905impl_typed_slice_cmp! {
906    name=FragmentBuf,
907    against=Chunk,
908}
909
910// -----------------------------------------------------------------------------
911impl_typed_slice_cmp! {
912    name=FragmentBuf,
913    against=Fragment,
914}
915
916// -----------------------------------------------------------------------------
917impl_typed_slice_cmp! {
918    name=FragmentBuf,
919    against=Ident,
920}