Skip to main content

typed_ident/alloc/
ident_buf.rs

1// =============================================================================
2// USES
3// =============================================================================
4
5// -----------------------------------------------------------------------------
6use crate::alloc::FragmentBuf;
7use crate::core::error::{Error, ErrorKind};
8use crate::core::{Chunk, Fragment, Ident};
9use crate::syntax::{Boundary, Delimiter, Profile, UnitDelimiter};
10use core::ops::{Bound, RangeBounds};
11use std_alloc::borrow::ToOwned;
12use std_alloc::boxed::Box;
13use std_alloc::collections::TryReserveError;
14use std_alloc::string::String;
15
16// =============================================================================
17// TYPES
18// =============================================================================
19
20/// A dynamic, growable identifier.
21///
22/// This allows you to build a identifier dynamically, instead of having to get
23/// one from a string slice.
24///
25/// # Not Inherently an `Ident`
26///
27/// Unlike [`Fragment`]s, [`Ident`]s have a requirement not only of being
28/// comprised of certain characters in a certain order, but *also* that the
29/// identifier itself is *not empty*.
30///
31/// Because of that, and because `IdentBuf` *can* be empty, it might surprise
32/// you to realize that `IdentBuf` does *NOT* implement `Deref` to `Ident` (as
33/// `String`, `PathBuf`, or indeed, `FragmentBuf` would for their respective
34/// immutably borrowed counterparts).
35///
36/// Instead, you must attempt a fallible cast to an identifier, which can only
37/// fail if the buffer itself is empty.
38///
39/// ```
40/// # use typed_ident::presets::unicode::upper_camel::*;
41/// let mut buffer = UpperCamelIdentBuf::new();
42/// assert!(buffer.as_ident().is_none());
43///
44/// buffer.push('V')?;
45/// assert!(buffer.as_ident().is_some());
46/// # Ok::<(), typed_ident::Error>(())
47/// ```
48///
49/// If your chosen delimiter implements `Default`, there's a nice helper
50/// function for this, which does what I believe most people would do in this
51/// situation; see [`as_ident_or_anonymous`].
52///
53/// [`as_ident_or_anonymous`]: Self::as_ident_or_anonymous
54pub struct IdentBuf<B, D, P> {
55    inner: FragmentBuf<B, D, P>,
56}
57
58// =============================================================================
59// IMPLS
60// =============================================================================
61
62// -----------------------------------------------------------------------------
63impl<B: Boundary, D: Delimiter, P: Profile> IdentBuf<B, D, P> {
64    #[must_use]
65    #[inline]
66    pub(crate) fn can_push_fragment_char(left: Option<&Ident<B, D, P>>, right: char) -> bool {
67        match left {
68            None => D::is_ident_start(right) || P::is_ident_start(right),
69            Some(left) => FragmentBuf::can_push_fragment_char(left, right),
70        }
71    }
72
73    #[must_use]
74    #[inline]
75    pub(crate) fn can_join(left: Option<&Ident<B, D, P>>, right: &Fragment<B, D, P>) -> bool {
76        right
77            .chars()
78            .next()
79            .is_none_or(|c| Self::can_push_fragment_char(left, c))
80    }
81
82    /// Constructs an ident buffer, initializing the contents to a provided
83    /// string slice (attempting first to convert the string slice to a valid
84    /// ident).
85    ///
86    /// This is equivalent to `IdentBuf::from_fragment(Fragment::new(s)?)`.
87    ///
88    /// # Examples
89    ///
90    /// Basic Usage:
91    ///
92    /// ```
93    /// # use typed_ident::*;
94    /// # use presets::unicode::upper_camel::UpperCamelIdentBuf;
95    /// assert!(UpperCamelIdentBuf::from_str("").is_err());
96    /// assert!(UpperCamelIdentBuf::from_str("ValidUpperCamel").is_ok());
97    /// assert!(UpperCamelIdentBuf::from_str("continuingUpperCamel").is_err());
98    /// assert!(UpperCamelIdentBuf::from_str("not_validUpperCamel").is_err());
99    /// # Ok::<(), Error>(())
100    /// ```
101    #[inline]
102    #[allow(clippy::should_implement_trait)] // It *does* implement the trait.
103    pub fn from_str(s: &str) -> Result<Self, Error> {
104        core::str::FromStr::from_str(s)
105    }
106
107    /// Constructs an ident buffer, initializing the contents to a provided
108    /// buffered string (checking first that the string is a valid ident).
109    ///
110    /// This is similar to [`from_str`], except that it will not allocate a
111    /// separate string. It will use the provided string, if it's valid.
112    ///
113    /// [`from_str`]: Self::from_str
114    ///
115    /// # Examples
116    ///
117    /// Basic Usage:
118    ///
119    /// ```
120    /// # use typed_ident::*;
121    /// # use presets::unicode::upper_camel::UpperCamelIdentBuf;
122    /// assert!(UpperCamelIdentBuf::from_string(String::from("")).is_err());
123    /// assert!(UpperCamelIdentBuf::from_string(String::from("ValidUpperCamel")).is_ok());
124    /// assert!(UpperCamelIdentBuf::from_string(String::from("continuingUpperCamel")).is_err());
125    /// assert!(UpperCamelIdentBuf::from_string(String::from("not_validUpperCamel")).is_err());
126    /// # Ok::<(), Error>(())
127    #[inline]
128    pub fn from_string(s: String) -> Result<Self, Error> {
129        let _ = Ident::<B, D, P>::new(&s)?;
130        Ok(Self::from_string_unchecked(s))
131    }
132
133    #[doc = include_str!("docs/methods/insert_fragment.md")]
134    #[doc = include_str!("docs/sections/panics.md")]
135    #[doc = include_str!("docs/sections/errors.md")]
136    ///
137    /// If the insertion targeted the beginning of the identifier, but the first
138    /// character was not a valid identifier start character, then the error
139    /// kind will be `InvalidFormat`.
140    ///
141    /// # Examples
142    ///
143    /// Basic Usage:
144    ///
145    /// ```
146    /// # use typed_ident::*;
147    /// # use presets::unicode::upper_camel::*;
148    /// let mut buffer = UpperCamelIdentBuf::from_str("UpperCamel")?;
149    ///
150    /// // Inserting at the beginning is ~prepend, and thus must be `UpperCamel`.
151    /// let mut example = buffer.clone();
152    /// assert!(example.insert_fragment(0, UpperCamelFragment::new("Valid")?).is_ok());
153    ///
154    /// // Inserting at the end is ~append, so it must not accidentally produce a `lowerCamel`.
155    /// let mut example = buffer.clone();
156    /// example.push('_')?;
157    /// assert!(example.insert_fragment(example.len(), UpperCamelFragment::new("invalid")?).is_err());
158    /// assert!(example.insert_fragment(example.len(), UpperCamelFragment::new("Valid")?).is_ok());
159    ///
160    /// // But note, that if it would simply append an ongoing chunk, that's fine.
161    /// let mut example = buffer.clone();
162    /// assert!(example.insert_fragment(example.len(), UpperCamelFragment::new("valid")?).is_ok());
163    ///
164    /// // Inserting in the middle is tricky, you must ensure it forms a valid `UpperCamel` ident.
165    /// let mut example = buffer.clone();
166    /// assert!(example.insert_fragment(2, UpperCamelFragment::new("_")?).is_err());
167    /// assert!(example.insert_fragment(5, UpperCamelFragment::new("_")?).is_ok());
168    /// # Ok::<(), Error>(())
169    /// ```
170    #[inline]
171    pub fn insert_fragment(
172        &mut self,
173        idx: usize,
174        fragment: &Fragment<B, D, P>,
175    ) -> Result<(), Error> {
176        let Some(first) = fragment.chars().next() else {
177            return Ok(());
178        };
179        if idx == 0 && !D::is_ident_start(first) && !P::is_ident_start(first) {
180            return Err(Error::new(ErrorKind::InvalidFormat).with_byte_offset(0));
181        }
182        self.inner.insert_fragment(idx, fragment)
183    }
184
185    #[doc = include_str!("docs/methods/insert_bounded_fragment.md")]
186    #[doc = include_str!("docs/sections/panics.md")]
187    #[doc = include_str!("docs/sections/errors.md")]
188    ///
189    /// If the insertion targeted the beginning of the identifier, but the first
190    /// character was not a valid identifier start character, then the error
191    /// kind will be `InvalidFormat`.
192    ///
193    /// # Examples
194    ///
195    /// Basic Usage:
196    ///
197    /// ```
198    /// # use typed_ident::*;
199    /// # use presets::unicode::upper_camel::*;
200    /// let mut buffer = UpperCamelIdentBuf::from_str("UpperCamel")?;
201    ///
202    /// // Inserting a character that needs no separation introduces no delims.
203    /// let mut example = buffer.clone();
204    /// assert!(example.insert_bounded_fragment(5, UpperCamelFragment::new("Formatted")?).is_ok());
205    /// assert_eq!(example, "UpperFormattedCamel");
206    ///
207    /// // Inserting a delimiter works as long as you don't break the formatting.
208    /// let mut example = buffer.clone();
209    /// assert!(example.insert_bounded_fragment(5, UpperCamelFragment::new("_")?).is_ok());
210    /// assert!(example.insert_bounded_fragment(2, UpperCamelFragment::new("_")?).is_err());
211    /// assert_eq!(example, "Upper_Camel");
212    ///
213    /// // It's most common to push characters onto the end though.
214    /// let mut example = buffer.clone();
215    /// assert!(example.insert_bounded_fragment(10, UpperCamelFragment::new("1")?).is_ok());
216    /// assert_eq!(example, "UpperCamel_1");
217    /// assert!(example.insert_bounded_fragment(12, UpperCamelFragment::new("FRAGMENT")?).is_ok());
218    /// assert_eq!(example, "UpperCamel_1_FRAGMENT");
219    /// # Ok::<(), Error>(())
220    /// ```
221    ///
222    /// There's an interesting case where fixing one side makes the other side
223    /// combine with the chunk to its left (only impacts certain syntaxes).
224    ///
225    /// ```
226    /// # use typed_ident::*;
227    /// # use presets::unicode::camel::*;
228    /// let mut buffer = CamelIdentBuf::from_str("UpperCamel")?;
229    /// assert!(buffer.insert_bounded_str(1, "U").is_ok());
230    /// assert_eq!(buffer, "U_U_pperCamel"); // Instead of "UU_pperCamel"
231    /// # Ok::<(), Error>(())
232    /// ```
233    #[inline]
234    pub fn insert_bounded_fragment_with(
235        &mut self,
236        idx: usize,
237        fragment: &Fragment<B, D, P>,
238        delim: D,
239    ) -> Result<(), Error> {
240        let Some(first) = fragment.chars().next() else {
241            return Ok(());
242        };
243        if idx == 0 && !D::is_ident_start(first) && !P::is_ident_start(first) {
244            return Err(Error::new(ErrorKind::InvalidFormat).with_byte_offset(0));
245        }
246        self.inner
247            .insert_bounded_fragment_with(idx, fragment, delim)
248    }
249
250    #[doc = include_str!("docs/methods/insert_delimited_fragment.md")]
251    #[doc = include_str!("docs/sections/panics.md")]
252    #[doc = include_str!("docs/sections/errors.md")]
253    ///
254    /// If the insertion targeted the beginning of the identifier, but the first
255    /// character was not a valid identifier start character, then the error
256    /// kind will be `InvalidFormat`.
257    ///
258    /// # Examples
259    ///
260    /// Basic Usage:
261    ///
262    /// ```
263    /// # use typed_ident::*;
264    /// # use typed_ident::syntax::delimiter::*;
265    /// # use presets::unicode::upper_camel::*;
266    /// let mut buffer = UpperCamelIdentBuf::from_str("UpperCamel")?;
267    ///
268    /// // Inserting a character that needs no separation introduces no delims.
269    /// let mut example = buffer.clone();
270    /// assert!(example.insert_delimited_fragment_with(5, UpperCamelFragment::new("Formatted")?, LowLine).is_ok());
271    /// assert_eq!(example, "Upper_Formatted_Camel");
272    ///
273    /// // Inserting a delimiter works as long as you don't break the formatting.
274    /// let mut example = buffer.clone();
275    /// assert!(example.insert_delimited_fragment_with(5, UpperCamelFragment::new("_")?, LowLine).is_ok());
276    /// assert!(example.insert_delimited_fragment_with(2, UpperCamelFragment::new("_")?, LowLine).is_err());
277    /// assert_eq!(example, "Upper_Camel");
278    ///
279    /// // It's most common to push characters onto the end though.
280    /// let mut example = buffer.clone();
281    /// assert!(example.insert_delimited_fragment_with(10, UpperCamelFragment::new("1")?, LowLine).is_ok());
282    /// assert_eq!(example, "UpperCamel_1");
283    /// assert!(example.insert_delimited_fragment_with(12, UpperCamelFragment::new("Fragment")?, LowLine).is_ok());
284    /// assert_eq!(example, "UpperCamel_1_Fragment");
285    /// # Ok::<(), Error>(())
286    /// ```
287    #[inline]
288    pub fn insert_delimited_fragment_with(
289        &mut self,
290        idx: usize,
291        fragment: &Fragment<B, D, P>,
292        delim: D,
293    ) -> Result<(), Error> {
294        let Some(first) = fragment.chars().next() else {
295            return Ok(());
296        };
297        if idx == 0 && !D::is_ident_start(first) && !P::is_ident_start(first) {
298            return Err(Error::new(ErrorKind::InvalidFormat).with_byte_offset(0));
299        }
300        self.inner
301            .insert_delimited_fragment_with(idx, fragment, delim)
302    }
303
304    #[doc = include_str!("docs/methods/push_delim.md")]
305    #[doc = include_str!("docs/methods/push_delim.errors.md")]
306    ///
307    /// If the insertion targeted the beginning of the identifier, but the first
308    /// character was not a valid identifier start character, then the error
309    /// kind will be `InvalidFormat`.
310    ///
311    /// # Examples
312    ///
313    /// Basic Usage:
314    ///
315    /// ```
316    /// # use typed_ident::*;
317    /// # use syntax::delimiter::LowLine;
318    /// # use presets::unicode::upper_camel::*;
319    /// let mut buffer = UpperCamelIdentBuf::new();
320    ///
321    /// // For all preset and provided delimiters, you can push them anywhere in
322    /// // an identifier. Unless you have a custom delimiter, it's always safe to push.
323    /// assert!(buffer.push_delim_with(LowLine).is_ok());
324    /// # Ok::<(), Error>(())
325    /// ```
326    ///
327    /// Example Failure:
328    ///
329    /// ```
330    /// # use typed_ident::*;
331    /// # use typed_ident::syntax::*;
332    /// # use syntax::delimiter::LowLine;
333    /// # use presets::unicode::upper_camel::*;
334    /// #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
335    /// struct DollarStart;
336    ///
337    /// impl Delimiter for DollarStart {
338    ///     fn as_char(&self) -> char {
339    ///         '$'
340    ///     }
341    ///     fn from_ident_start(c: char) -> Option<Self> {
342    ///         match c {
343    ///             '$' => Some(Self),
344    ///             _ => None,
345    ///         }
346    ///     }
347    ///     fn from_chunk_delim(c: char) -> Option<Self> {
348    ///         None
349    ///     }
350    /// }
351    ///
352    /// type DollarStartIdentBuf = IdentBuf<
353    ///     boundary::Standard,
354    ///     DollarStart,
355    ///     profile::Unicode,
356    /// >;
357    ///
358    /// let mut buffer = DollarStartIdentBuf::new();
359    ///
360    /// // Okay to push one `$` in, because it may be the start fragment.
361    /// assert!(buffer.push_delim_with(DollarStart).is_ok());
362    ///
363    /// // But you definitely cannot push another in - that's invalid.
364    /// assert!(buffer.push_delim_with(DollarStart).is_err());
365    /// # Ok::<(), Error>(())
366    /// ```
367    #[inline]
368    pub fn push_delim_with(&mut self, delim: D) -> Result<(), Error> {
369        if !D::APPEND_CLOSED.at_least_identifier()
370            && self.inner.is_empty()
371            && !D::is_ident_start(delim.as_char())
372        {
373            return Err(Error::new(ErrorKind::InvalidFormat).with_byte_offset(0));
374        }
375        self.inner.push_delim_with(delim)
376    }
377
378    #[doc = include_str!("docs/methods/remove.md")]
379    #[doc = include_str!("docs/sections/panics.md")]
380    ///
381    /// # Errors
382    ///
383    /// If the removal of the character at the provided index would lead to an
384    /// invalid buffer, then the character will not be remove and instead the
385    /// error `FailedRemove` will be returned.
386    ///
387    /// # Examples
388    ///
389    /// Basic Usage:
390    ///
391    /// ```
392    /// # use typed_ident::*;
393    /// # use typed_ident::syntax::delimiter::*;
394    /// # use presets::unicode::upper_camel::UpperCamelFragmentBuf;
395    /// let mut buffer = UpperCamelFragmentBuf::from_str("Upper_Camel")?;
396    ///
397    /// // This would be valid, because it might be a continuation fragment.
398    /// assert!(buffer.remove(0).is_ok());
399    /// assert_eq!(buffer, "pper_Camel");
400    ///
401    /// // However, attempting to remove `C` would fail for `UpperCamel`.
402    /// assert!(buffer.remove(5).is_err());
403    /// assert_eq!(buffer, "pper_Camel");
404    /// # Ok::<(), Error>(())
405    /// ```
406    #[inline]
407    pub fn remove(&mut self, idx: usize) -> Result<(), Error> {
408        let (left, right) = self
409            .as_ident()
410            .expect(
411                "attempted to remove a character from an identifier buffer that was out of bounds",
412            )
413            .split_at(idx);
414        let mut chars = right.chars();
415        let _ = chars.next();
416        let right = chars.as_fragment();
417        if !Self::can_join(left, right) {
418            return Err(Error::new(ErrorKind::FailedRemove).with_byte_offset(idx));
419        }
420        self.remove_unchecked(idx);
421        Ok(())
422    }
423
424    #[doc = include_str!("docs/methods/replace_range.md")]
425    #[doc = include_str!("docs/sections/panics.md")]
426    ///
427    /// # Errors
428    ///
429    /// If the replacement of the range provided with the given fragment would
430    /// lead to an invalid buffer, then the range will not be remove and instead
431    /// the error `InvalidReplace` will be returned.
432    ///
433    /// If the insertion targeted the beginning of the identifier, but the first
434    /// character was not a valid identifier start character, then the error
435    /// kind will be `InvalidFormat`.
436    ///
437    /// # Examples
438    ///
439    /// Basic Usage:
440    ///
441    /// ```
442    /// # use typed_ident::*;
443    /// # use typed_ident::syntax::delimiter::*;
444    /// # use presets::unicode::upper_camel::*;
445    /// let buffer = UpperCamelIdentBuf::from_str("Upper_Camel")?;
446    ///
447    /// // Examples replacing various ranges.
448    /// let replacement = UpperCamelFragment::new("R")?;
449    /// let mut example = buffer.clone();
450    /// assert!(example.replace_range_fragment(4..7, replacement).is_ok());
451    /// assert_eq!(example, "UppeRamel");
452    ///
453    /// let mut example = buffer.clone();
454    /// assert!(example.replace_range_fragment(4..=7, replacement).is_ok());
455    /// assert_eq!(example, "UppeRmel");
456    ///
457    /// let mut example = buffer.clone();
458    /// assert!(example.replace_range_fragment(..7, replacement).is_ok());
459    /// assert_eq!(example, "Ramel");
460    ///
461    /// let mut example = buffer.clone();
462    /// assert!(example.replace_range_fragment(..=7, replacement).is_ok());
463    /// assert_eq!(example, "Rmel");
464    ///
465    /// let mut example = buffer.clone();
466    /// assert!(example.replace_range_fragment(4.., replacement).is_ok());
467    /// assert_eq!(example, "UppeR");
468    /// # Ok::<(), Error>(())
469    /// ```
470    #[inline]
471    pub fn replace_range_fragment<R>(
472        &mut self,
473        range: R,
474        replace_with: &Fragment<B, D, P>,
475    ) -> Result<(), Error>
476    where
477        R: RangeBounds<usize>,
478    {
479        let start = match range.start_bound() {
480            Bound::Included(idx) => *idx,
481            Bound::Excluded(idx) => *idx + 1,
482            Bound::Unbounded => 0,
483        };
484        if start == 0 {
485            let end = match range.end_bound() {
486                Bound::Included(idx) => *idx + 1,
487                Bound::Excluded(idx) => *idx,
488                Bound::Unbounded => self.len(),
489            };
490            let valid_first_char = replace_with
491                .chars()
492                .next()
493                .or_else(|| self[end..].chars().next())
494                .is_none_or(|c| D::is_ident_start(c) || P::is_ident_start(c));
495            if !valid_first_char {
496                return Err(Error::new(ErrorKind::InvalidFormat).with_byte_offset(0));
497            }
498        }
499        self.inner.replace_range_fragment(range, replace_with)
500    }
501
502    #[doc = include_str!("docs/methods/split_off.md")]
503    #[doc = include_str!("docs/sections/panics.md")]
504    ///
505    /// # Examples
506    ///
507    /// Basic Usage:
508    ///
509    /// ```
510    /// # use typed_ident::*;
511    /// # use typed_ident::syntax::delimiter::*;
512    /// # use presets::unicode::upper_camel::*;
513    /// let mut buffer = UpperCamelIdentBuf::from_str("UpperCamel")?;
514    /// let split = buffer.split_off(5);
515    /// assert_eq!(buffer, "Upper");
516    /// assert_eq!(split, "Camel");
517    /// # Ok::<(), Error>(())
518    /// ```
519    #[must_use]
520    #[inline]
521    pub fn split_off(&mut self, idx: usize) -> FragmentBuf<B, D, P> {
522        self.inner.split_off(idx)
523    }
524}
525
526// -----------------------------------------------------------------------------
527impl<B, D, P> IdentBuf<B, D, P> {
528    /// Fallibly casts the buffer to an identifier.
529    ///
530    /// This can fail because the buffer can be empty. As long as the buffer is
531    /// *not* empty, than this will succeed (because this type enforces that
532    /// what is added to the buffer conforms to the requirements of a valid
533    /// identifier).
534    ///
535    /// # Examples
536    ///
537    /// Basic Usage:
538    ///
539    /// ```
540    /// # use typed_ident::presets::unicode::lower_camel::*;
541    /// let mut buffer = LowerCamelIdentBuf::new();
542    /// assert_eq!(buffer.as_ident(), None);
543    ///
544    /// buffer.push_str("ident")?;
545    /// assert_eq!(buffer.as_ident(), Some(LowerCamelIdent::new("ident")?));
546    /// # Ok::<(), typed_ident::Error>(())
547    /// ```
548    #[must_use]
549    #[inline]
550    pub fn as_ident(&self) -> Option<&Ident<B, D, P>> {
551        match self.is_empty() {
552            true => None,
553            false => Some(Ident::new_unchecked(self.inner.as_str())),
554        }
555    }
556
557    /// Either returns the identifier contained by this buffer (if non-empty),
558    /// or return the provided default identifier.
559    ///
560    /// This is equivalent to `as_ident().unwrap_or(_)`, but it's provided for
561    /// convenience and to parallel the [`as_ident_or_anonymous`].
562    ///
563    /// [`as_ident_or_anonymous`]: Self::as_ident_or_anonymous
564    ///
565    /// # Examples
566    ///
567    /// Basic Usage:
568    ///
569    /// ```
570    /// # use typed_ident::presets::unicode::lower_camel::*;
571    /// let fallback = LowerCamelIdent::new("fallback")?;
572    /// let mut buffer = LowerCamelIdentBuf::new();
573    /// assert_eq!(buffer.as_ident_or(fallback), fallback);
574    ///
575    /// buffer.push_str("ident")?;
576    /// assert_eq!(buffer.as_ident_or(fallback), "ident");
577    /// # Ok::<(), typed_ident::Error>(())
578    /// ```
579    #[must_use]
580    #[inline]
581    pub fn as_ident_or<'a>(&'a self, default: &'a Ident<B, D, P>) -> &'a Ident<B, D, P> {
582        match self.is_empty() {
583            true => default,
584            false => Ident::new_unchecked(self.inner.as_str()),
585        }
586    }
587
588    /// Either returns the identifier contained by this buffer (if non-empty),
589    /// or return a single delimiter representing an anonymous value.
590    ///
591    /// This can only be called if it's obvious which delimiter should be
592    /// provided, and that is only possible for [`UnitDelimiter`] delimiters
593    /// (Like [`LowLine`] and [`HyphenMinus`]).
594    ///
595    /// [`HyphenMinus`]: crate::syntax::delimiter::HyphenMinus
596    /// [`LowLine`]: crate::syntax::delimiter::LowLine
597    /// [`UnitDelimiter`]: crate::syntax::delimiter::UnitDelimiter
598    ///
599    /// # Examples
600    ///
601    /// Basic Usage:
602    ///
603    /// ```
604    /// # use typed_ident::presets::unicode::lower_camel::*;
605    /// let mut buffer = LowerCamelIdentBuf::new();
606    /// assert_eq!(buffer.as_ident_or_anonymous(), "_");
607    ///
608    /// buffer.push_str("ident")?;
609    /// assert_eq!(buffer.as_ident_or_anonymous(), "ident");
610    /// # Ok::<(), typed_ident::Error>(())
611    /// ```
612    #[must_use]
613    #[inline]
614    pub fn as_ident_or_anonymous(&self) -> &Ident<B, D, P>
615    where
616        D: UnitDelimiter,
617    {
618        match self.is_empty() {
619            true => Ident::new_unchecked(D::STR),
620            false => Ident::new_unchecked(self.inner.as_str()),
621        }
622    }
623
624    /// Converts an ident into an ident buffer.
625    ///
626    /// # Examples
627    ///
628    /// ```
629    /// # use typed_ident::*;
630    /// # use presets::unicode::upper_camel::*;
631    /// let ident = UpperCamelIdent::new("Example")?;
632    /// let mut buffer = UpperCamelIdentBuf::from_ident(ident);
633    /// assert_eq!(buffer, "Example");
634    /// # Ok::<(), Error>(())
635    /// ```
636    #[must_use]
637    #[inline]
638    pub fn from_ident(orig: &Ident<B, D, P>) -> Self {
639        Self {
640            inner: FragmentBuf::from_fragment(orig.as_fragment()),
641        }
642    }
643
644    /// Converts an allocated string into an ident buffer, without checking if
645    /// the allocated string is a valid identifier or not.
646    ///
647    /// # Safety
648    ///
649    /// You can only call this if the input string is from a valid [`Ident`]
650    /// over the same generic parameters, or if you have ensured the string
651    /// *would* have been valid.
652    ///
653    /// Needless to say, this is difficult to know unless you are taking a
654    /// prefixed slice of an existing ident, or if you are testing this at
655    /// compile time.
656    ///
657    /// [`Ident`]: crate::core::Ident
658    #[must_use]
659    #[inline]
660    pub(crate) fn from_string_unchecked(s: String) -> Self {
661        Self {
662            inner: FragmentBuf::from_string_unchecked(s),
663        }
664    }
665
666    /// Convert the buffer into a boxed identifier (if possible).
667    ///
668    /// This can fail if the buffer is empty.
669    ///
670    /// # Examples
671    ///
672    /// ```
673    /// # use typed_ident::presets::unicode::hybrid::*;
674    /// let mut buffer = HybridIdentBuf::from_str("example")?;
675    /// let boxed: Box<HybridIdent> = buffer.into_boxed_ident().unwrap();
676    /// assert_eq!(boxed.as_ref(), "example");
677    /// # Ok::<(), typed_ident::Error>(())
678    /// ```
679    #[must_use]
680    #[inline]
681    pub fn into_boxed_ident(self) -> Option<Box<Ident<B, D, P>>> {
682        if self.inner.is_empty() {
683            return None;
684        }
685        Some(Ident::new_boxed_unchecked(self.into_string()))
686    }
687
688    /// Convert the buffer into an owned string.
689    ///
690    /// # Examples
691    ///
692    /// ```
693    /// # use typed_ident::presets::unicode::upper_camel::*;
694    /// let buffer = UpperCamelIdentBuf::from_str("Example")?;
695    /// let string: String = buffer.into_string();
696    /// assert_eq!(string, "Example");
697    /// # Ok::<(), typed_ident::Error>(())
698    /// ```
699    #[must_use]
700    #[inline]
701    pub fn into_string(self) -> String {
702        self.inner.into_string()
703    }
704
705    /// Leaks the identifier so that it lives for the rest of the execution of
706    /// the program.
707    ///
708    /// This can fail if the buffer is empty.
709    ///
710    /// This is a typed wrapper over the [`String::leak`] method.
711    ///
712    /// # Examples
713    ///
714    /// ```no_run
715    /// # use typed_ident::presets::unicode::upper_camel::*;
716    /// let fragment = UpperCamelIdentBuf::from_str("Example")?;
717    /// let string: &'static UpperCamelFragment = fragment.leak().unwrap();
718    /// # Ok::<(), typed_ident::Error>(())
719    /// ```
720    #[must_use]
721    #[inline]
722    pub fn leak<'a>(self) -> Option<&'a Ident<B, D, P>> {
723        match self.is_empty() {
724            true => None,
725            false => Some(Ident::new_unchecked(self.inner.into_string().leak())),
726        }
727    }
728
729    #[inline]
730    pub(crate) fn remove_unchecked(&mut self, idx: usize) {
731        self.inner.remove_unchecked(idx)
732    }
733
734    /// Constructs an empty ident buffer with an initial capacity.
735    ///
736    /// This has the same properties as [`String::with_capacity`].
737    ///
738    /// # Examples
739    ///
740    /// ```
741    /// # use typed_ident::presets::unicode::upper_camel::*;
742    /// let buffer = UpperCamelIdentBuf::with_capacity(10);
743    /// assert!(buffer.capacity() >= 10);
744    /// # Ok::<(), typed_ident::Error>(())
745    /// ```
746    #[must_use]
747    #[inline]
748    pub fn with_capacity(capacity: usize) -> Self {
749        Self {
750            inner: FragmentBuf::with_capacity(capacity),
751        }
752    }
753
754    /// Constructs an ident buffer with enough space to hold the provided ident,
755    /// as well as `additional` bytes, then initializes the contents of this
756    /// buffer to `ident`.
757    ///
758    /// # Examples
759    ///
760    /// ```
761    /// # use typed_ident::presets::unicode::upper_camel::*;
762    /// let ident = UpperCamelIdent::new("Example")?;
763    /// let buffer = UpperCamelIdentBuf::with_overhead(ident, 20);
764    /// assert!(buffer.capacity() >= 27);
765    /// assert_eq!(buffer, "Example");
766    /// # Ok::<(), typed_ident::Error>(())
767    /// ```
768    #[must_use]
769    #[inline]
770    pub fn with_overhead(ident: &Ident<B, D, P>, additional: usize) -> Self {
771        Self {
772            inner: FragmentBuf::with_overhead(ident, additional),
773        }
774    }
775}
776
777// -----------------------------------------------------------------------------
778impl_buffer_methods! {
779    name=IdentBuf,
780}
781
782// =============================================================================
783// TRAIT IMPL
784// =============================================================================
785
786// -----------------------------------------------------------------------------
787impl<B: Boundary, D: Delimiter, P: Profile> core::str::FromStr for IdentBuf<B, D, P> {
788    type Err = Error;
789    #[inline]
790    fn from_str(s: &str) -> Result<Self, Self::Err> {
791        Ok(Self::from_ident(Ident::new(s)?))
792    }
793}
794
795// -----------------------------------------------------------------------------
796impl<'a, B, D, P> core::convert::From<&'a Ident<B, D, P>> for IdentBuf<B, D, P> {
797    #[inline]
798    fn from(orig: &'a Ident<B, D, P>) -> Self {
799        Self::from_ident(orig)
800    }
801}
802
803// -----------------------------------------------------------------------------
804impl<B, D, P> Clone for IdentBuf<B, D, P> {
805    #[inline]
806    fn clone(&self) -> Self {
807        Self {
808            inner: self.inner.clone(),
809        }
810    }
811}
812
813// -----------------------------------------------------------------------------
814impl<B, D, P> ToOwned for Ident<B, D, P> {
815    type Owned = Box<Ident<B, D, P>>;
816    #[inline]
817    fn to_owned(&self) -> Self::Owned {
818        IdentBuf::from(self)
819            .into_boxed_ident()
820            .expect("it should never be the case that an ident fails conversion to a boxed ident")
821    }
822}
823
824// -----------------------------------------------------------------------------
825impl_buffer_traits! {
826    name=IdentBuf,
827}
828
829// -----------------------------------------------------------------------------
830impl_typed_slice_traits! {
831    name=IdentBuf,
832    index_target=Fragment,
833}
834
835// -----------------------------------------------------------------------------
836impl_typed_slice_cmp! {
837    name=IdentBuf,
838    against=Chunk,
839}
840
841// -----------------------------------------------------------------------------
842impl_typed_slice_cmp! {
843    name=IdentBuf,
844    against=Fragment,
845}
846
847// -----------------------------------------------------------------------------
848impl_typed_slice_cmp! {
849    name=IdentBuf,
850    against=FragmentBuf,
851}
852
853// -----------------------------------------------------------------------------
854impl_typed_slice_cmp! {
855    name=IdentBuf,
856    against=Ident,
857}