Skip to main content

typed_ident/alloc/
ident.rs

1// =============================================================================
2// USES
3// =============================================================================
4
5// -----------------------------------------------------------------------------
6use crate::core::error::{Error, ErrorKind};
7use crate::syntax::{Boundary, Delimiter, Profile};
8use crate::{Fragment, FragmentBuf, Ident, IdentBuf};
9use std_alloc::boxed::Box;
10use std_alloc::string::String;
11
12// =============================================================================
13// IMPLS
14// =============================================================================
15
16// -----------------------------------------------------------------------------
17impl<B: Boundary, D: Delimiter, P: Profile> Ident<B, D, P> {
18    /// Returns a heap-allocated identifier, joined with the original identifier
19    /// in a way that preserves chunk boundaries.
20    ///
21    /// At the end of the operation, the total number of chunked segments
22    /// present in the fragment will be equal to the sum of each fragment,
23    /// potentially plus one additional fragment in the case where we needed to
24    /// join using a delimiter to preserve chunk boundaries.
25    ///
26    /// This call is identical to [`join_with`] with the default delimiter.
27    ///
28    /// It can be a bit cumbersome to use this function in most cases. Instead,
29    /// if you find it easier to work with string data (or you don't have any
30    /// fragments that you're joining with), you can use [`join_str`].
31    ///
32    /// [`join_str`]: Self::join_str
33    /// [`join_with`]: Self::join_with
34    ///
35    /// # Errors
36    ///
37    /// Returns `Err` if the fragment formed from the combination of `self` and
38    /// `fragment` is invalid. If invalid, an [`Error`] is returned with the
39    /// [`error_kind`] set to `FailedLeftJoin`.
40    ///
41    /// The value [`byte_offset`] will *NOT* be set from this function. None of
42    /// the individual characters are invalid, it's just that the combination of
43    /// joining the fragments themselves is invalid.
44    ///
45    /// [`Error`]: crate::Error
46    /// [`error_kind`]: crate::Error::error_kind
47    /// [`byte_offset`]: crate::Error::byte_offset
48    ///
49    /// # Examples
50    ///
51    /// Basic Usage:
52    ///
53    /// ```
54    /// # use typed_ident::presets::unicode::lower_snake::*;
55    /// let ident = LowerSnakeIdent::new("snake")?;
56    /// let ident = ident.join(
57    ///     LowerSnakeFragment::new("ident")?,
58    /// )?;
59    /// assert_eq!(ident.as_ref(), "snake_ident");
60    /// # Ok::<(), typed_ident::Error>(())
61    /// ```
62    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
63    #[inline]
64    pub fn join(
65        &self,
66        fragment: &Fragment<B, D, P>,
67    ) -> Result<std_alloc::boxed::Box<Ident<B, D, P>>, Error>
68    where
69        D: Default,
70    {
71        self.join_with(fragment, D::default())
72    }
73
74    /// Returns a heap-allocated identifier, joined with the original identifier
75    /// in a way that preserves chunk boundaries. The provided string is first
76    /// converted to a fragment before attempting to append it.
77    ///
78    /// At the end of the operation, the total number of chunked segments
79    /// present in the fragment will be equal to the sum of each fragment,
80    /// potentially plus one additional fragment in the case where we needed to
81    /// join using a delimiter to preserve chunk boundaries.
82    ///
83    /// This call is identical to [`join_str_with`] with the default delimiter.
84    ///
85    /// [`join_str_with`]: Self::join_str_with
86    ///
87    /// # Errors
88    ///
89    /// Returns `Err` if the fragment formed from the combination of `self` and
90    /// `fragment` is invalid. If invalid, an [`Error`] is returned with the
91    /// [`error_kind`] set to `FailedLeftJoin`.
92    ///
93    /// The value [`byte_offset`] *MAY* be set on this function. If the joining
94    /// string contained invalid characters, this will be set to the byte index
95    /// (from the start of the joining string) that was invalid.
96    ///
97    /// However, if all characters are independently valid, but one side failed
98    /// to join (because the join itself would make the following character
99    /// invalid), then `byte_offset` will be set to `None`.
100    ///
101    /// [`Error`]: crate::Error
102    /// [`error_kind`]: crate::Error::error_kind
103    /// [`byte_offset`]: crate::Error::byte_offset
104    ///
105    /// # Examples
106    ///
107    /// Basic Usage:
108    ///
109    /// ```
110    /// # use typed_ident::presets::unicode::lower_snake::*;
111    /// let ident = LowerSnakeIdent::new("snake")?;
112    /// let ident = ident.join_str("ident")?;
113    /// assert_eq!(ident.as_ref(), "snake_ident");
114    /// # Ok::<(), typed_ident::Error>(())
115    /// ```
116    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
117    #[inline]
118    pub fn join_str(&self, s: &str) -> Result<Box<Ident<B, D, P>>, Error>
119    where
120        D: Default,
121    {
122        self.join_with(Fragment::new(s)?, D::default())
123    }
124
125    /// Returns a heap-allocated identifier, joined with the original identifier
126    /// in a way that preserves chunk boundaries. The provided string is first
127    /// converted to a fragment before attempting to append it.
128    ///
129    /// At the end of the operation, the total number of chunked segments
130    /// present in the identifier will be equal to the sum of each fragment,
131    /// potentially plus one additional fragment in the case where we needed to
132    /// join using a delimiter to preserve chunk boundaries.
133    ///
134    /// # Errors
135    ///
136    /// Returns `Err` if the fragment formed from the combination of `self` and
137    /// `fragment` is invalid. If invalid, an [`Error`] is returned with the
138    /// [`error_kind`] set to `InvalidFormat` or `FailedLeftJoin`.
139    ///
140    /// The value [`byte_offset`] *MAY* be set on this function. If the joining
141    /// string contained invalid characters, this will be set to the byte index
142    /// (from the start of the joining string) that was invalid.
143    ///
144    /// However, if all characters are independently valid, but one side failed
145    /// to join (because the join itself would make the following character
146    /// invalid), then `byte_offset` will be set to `None`.
147    ///
148    /// [`Error`]: crate::Error
149    /// [`error_kind`]: crate::Error::error_kind
150    /// [`byte_offset`]: crate::Error::byte_offset
151    ///
152    /// # Examples
153    ///
154    /// Basic Usage:
155    ///
156    /// ```
157    /// # use typed_ident::syntax::delimiter::LowLine;
158    /// # use typed_ident::presets::unicode::lower_snake::*;
159    /// let ident = LowerSnakeIdent::new("snake")?;
160    /// let ident = ident.join_str_with("ident", LowLine)?;
161    /// assert_eq!(ident.as_ref(), "snake_ident");
162    /// # Ok::<(), typed_ident::Error>(())
163    /// ```
164    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
165    #[inline]
166    pub fn join_str_with(&self, s: &str, delim: D) -> Result<Box<Ident<B, D, P>>, Error> {
167        self.join_with(Fragment::new(s)?, delim)
168    }
169
170    /// Returns a heap-allocated identifier, joined with the original identifier
171    /// in a way that preserves chunk boundaries.
172    ///
173    /// At the end of the operation, the total number of chunked segments
174    /// present in the identifier will be equal to the sum of each fragment,
175    /// potentially plus one additional fragment in the case where we needed to
176    /// join using a delimiter to preserve chunk boundaries.
177    ///
178    /// It can be a bit cumbersome to use this function in most cases. Instead,
179    /// if you find it easier to work with string data (or you don't have any
180    /// fragments that you're joining with), you can use [`join_str_with`].
181    ///
182    /// [`join_str_with`]: Self::join_str_with
183    ///
184    /// # Errors
185    ///
186    /// Returns `Err` if the fragment formed from the combination of `self` and
187    /// `fragment` is invalid. If invalid, an [`Error`] is returned with the
188    /// [`error_kind`] set to `FailedLeftJoin`.
189    ///
190    /// The value [`byte_offset`] will *NOT* be set from this function. None of
191    /// the individual characters are invalid, it's just that the combination of
192    /// joining the fragments themselves is invalid.
193    ///
194    /// [`Error`]: crate::Error
195    /// [`error_kind`]: crate::Error::error_kind
196    /// [`byte_offset`]: crate::Error::byte_offset
197    ///
198    /// # Examples
199    ///
200    /// Basic Usage:
201    ///
202    /// ```
203    /// # use typed_ident::syntax::delimiter::LowLine;
204    /// # use typed_ident::presets::unicode::lower_snake::*;
205    /// let ident = LowerSnakeIdent::new("snake")?;
206    /// let ident = ident.join_with(
207    ///     LowerSnakeFragment::new("ident")?,
208    ///     LowLine,
209    /// )?;
210    /// assert_eq!(ident.as_ref(), "snake_ident");
211    /// # Ok::<(), typed_ident::Error>(())
212    /// ```
213    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
214    #[inline]
215    pub fn join_with(
216        &self,
217        fragment: &Fragment<B, D, P>,
218        delim: D,
219    ) -> Result<Box<Ident<B, D, P>>, Error> {
220        let mut ident = IdentBuf::with_overhead(self, fragment.len() + 1);
221        ident.push_bounded_fragment_with(fragment, delim)?;
222        Ok(ident.into_boxed_ident().unwrap())
223    }
224
225    /// Converts a string into a boxed identifier if its valid.
226    ///
227    /// # Examples
228    ///
229    /// Basic Usage:
230    ///
231    /// ```
232    /// # use typed_ident::*;
233    /// # use typed_ident::presets::unicode::lower_snake::*;
234    /// let ident: Box<LowerSnakeIdent> =
235    ///     Ident::new_boxed(String::from("snake_ident"))?;
236    /// # Ok::<(), typed_ident::Error>(())
237    /// ```
238    #[inline]
239    pub fn new_boxed(string: String) -> Result<Box<Ident<B, D, P>>, Error> {
240        let _ = Ident::<B, D, P>::new(&string)?;
241        Ok(Self::new_boxed_unchecked(string))
242    }
243
244    /// Returns a heap-allocated identifier, replacing the provided pattern with
245    /// a fragment of the user's choice.
246    ///
247    /// It can be a bit cumbersome to use this function in most cases. Instead,
248    /// if you find it easier to work with string data (or you don't have any
249    /// fragments that you're replacing with), you can use [`replace_str`].
250    ///
251    /// [`replace_str`]: Self::replace_str
252    ///
253    /// # Errors
254    ///
255    /// Returns `Err` if the fragment formed from the combination of `self` and
256    /// `to` is invalid at any replacement index. If invalid, an [`Error`] is
257    /// returned with the [`error_kind`] set to `FailedReplaceLeft` or
258    /// `FailedReplaceRight` (if the replacement succeeded, but the remainder
259    /// could not be appended).
260    ///
261    /// The value [`byte_offset`] *WILL* be set from this function, and it will
262    /// be set to the index that caused the failure from the original fragment
263    /// (`self`).
264    ///
265    /// [`Error`]: crate::Error
266    /// [`error_kind`]: crate::Error::error_kind
267    /// [`byte_offset`]: crate::Error::byte_offset
268    ///
269    /// # Examples
270    ///
271    /// Basic Usage:
272    ///
273    /// ```
274    /// # use typed_ident::syntax::delimiter::LowLine;
275    /// # use typed_ident::presets::unicode::lower_snake::*;
276    /// let ident = LowerSnakeIdent::new("example_snake_identifier")?;
277    /// let ident = ident.replace(
278    ///     "snake",
279    ///     LowerSnakeFragment::new("serpent")?,
280    /// )?;
281    /// assert_eq!(ident.as_ident_or_anonymous(), "example_serpent_identifier");
282    /// # Ok::<(), typed_ident::Error>(())
283    /// ```
284    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
285    #[inline]
286    pub fn replace<M>(&self, from: M, to: &Fragment<B, D, P>) -> Result<IdentBuf<B, D, P>, Error>
287    where
288        M: crate::core::pattern::Pattern,
289    {
290        let mut buffer = IdentBuf::with_capacity(self.len());
291        let mut last_end = 0;
292        for (start, part) in self.match_indices(from) {
293            buffer.push_fragment(&self[last_end..start]).map_err(|_| {
294                Error::new(ErrorKind::FailedReplaceRight).with_byte_offset(last_end)
295            })?;
296            buffer
297                .push_fragment(to)
298                .map_err(|_| Error::new(ErrorKind::FailedReplaceLeft).with_byte_offset(start))?;
299            last_end = start + part.len();
300        }
301        buffer
302            .push_fragment(&self[last_end..self.len()])
303            .map_err(|_| Error::new(ErrorKind::FailedReplaceRight).with_byte_offset(last_end))?;
304        Ok(buffer)
305    }
306
307    /// Returns a heap-allocated identifier, replacing the provided pattern with
308    /// a fragment of the user's choice. The provided string is first converted
309    /// to a fragment before attempting to append it.
310    ///
311    /// It can be a bit cumbersome to use this function in most cases. Instead,
312    /// if you find it easier to work with string data (or you don't have any
313    /// fragments that you're replacing with), you can use [`replace_str`].
314    ///
315    /// [`replace_str`]: Self::replace_str
316    ///
317    /// # Errors
318    ///
319    /// Returns `Err` if the fragment formed from the combination of `self` and
320    /// `to` is invalid at any replacement index. If invalid, an [`Error`] is
321    /// returned with the [`error_kind`] set to `InvalidFormat` if the
322    /// provided fragment was invalid, or `InvalidReplace` if the replacement
323    /// failed.
324    ///
325    /// The value [`byte_offset`] *WILL* be set from this function. On invalid
326    /// fragment, it will be set to the byte index from the start of the
327    /// fragment which was invalid. On invalid replacement, it will be set to
328    /// the byte index that caused the failure from the original fragment
329    /// (`self`).
330    ///
331    /// [`Error`]: crate::Error
332    /// [`error_kind`]: crate::Error::error_kind
333    /// [`byte_offset`]: crate::Error::byte_offset
334    ///
335    /// # Examples
336    ///
337    /// Basic Usage:
338    ///
339    /// ```
340    /// # use typed_ident::syntax::delimiter::LowLine;
341    /// # use typed_ident::presets::unicode::lower_snake::*;
342    /// let ident = LowerSnakeIdent::new("example_snake_identifier")?;
343    /// let ident = ident.replace_str("snake", "serpent")?;
344    /// assert_eq!(ident.as_ident_or_anonymous(), "example_serpent_identifier");
345    /// # Ok::<(), typed_ident::Error>(())
346    /// ```
347    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
348    #[inline]
349    pub fn replace_str<M>(&self, from: M, to: &str) -> Result<IdentBuf<B, D, P>, Error>
350    where
351        M: crate::core::pattern::Pattern,
352    {
353        self.replace(from, Fragment::new(to)?)
354    }
355
356    /// Returns a heap-allocated identifier with the provided prefix and suffix
357    /// attached to the original identifier.
358    ///
359    /// It can be a bit cumbersome to use this function in most cases. Instead,
360    /// if you find it easier to work with string data (or you don't have any
361    /// fragments that you're joining with), you can use [`with_circumfix_str`].
362    ///
363    /// [`with_circumfix_str`]: Self::with_circumfix_str
364    ///
365    /// # Errors
366    ///
367    /// Returns `Err` if the fragment formed from the combination of `prefix`,
368    /// `self`, and `suffix` is invalid. If invalid, an [`Error`] is returned
369    /// with the [`error_kind`] set either to `InvalidPrefix` or `InvalidSuffix`
370    /// (depending on which has caused the failure).
371    ///
372    /// The value [`byte_offset`] will *NOT* be set from this function. None of
373    /// the individual characters are invalid, it's just that the combination of
374    /// joining the fragments themselves is invalid.
375    ///
376    /// [`Error`]: crate::Error
377    /// [`error_kind`]: crate::Error::error_kind
378    /// [`byte_offset`]: crate::Error::byte_offset
379    ///
380    /// # Examples
381    ///
382    /// Basic Usage:
383    ///
384    /// ```
385    /// # use typed_ident::presets::unicode::lower_snake::*;
386    /// let ident = LowerSnakeIdent::new("snake")?;
387    /// let ident = ident.with_circumfix(
388    ///     LowerSnakeFragment::new("lower_")?,
389    ///     LowerSnakeFragment::new("_ident")?,
390    /// )?;
391    /// assert_eq!(ident.as_ref(), "lower_snake_ident");
392    /// # Ok::<(), typed_ident::Error>(())
393    /// ```
394    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
395    #[inline]
396    pub fn with_circumfix(
397        &self,
398        prefix: &Fragment<B, D, P>,
399        suffix: &Fragment<B, D, P>,
400    ) -> Result<Box<Ident<B, D, P>>, Error> {
401        let mut ident = IdentBuf::with_capacity(self.len() + prefix.len() + suffix.len());
402        ident.push_fragment(prefix)?;
403        ident.push_fragment(self)?;
404        ident.push_fragment(suffix)?;
405        Ok(ident.into_boxed_ident().unwrap())
406    }
407
408    /// Returns a heap-allocated identifier with the provided prefix and suffix
409    /// strings attached to the original identifier. The provided strings are
410    /// first converted to fragments before attempting to append them.
411    ///
412    /// # Errors
413    ///
414    /// Returns `Err` if the fragment formed from the combination of `prefix`,
415    /// `self`, and `suffix` is invalid. If invalid, an [`Error`] is returned
416    /// with the [`error_kind`] set either to `InvalidPrefix` or `InvalidSuffix`
417    /// (depending on which has caused the failure).
418    ///
419    /// The value [`byte_offset`] *MAY* be set on this function. If the prefix
420    /// or suffix strings contained invalid characters, this will be set to the
421    /// byte index (from the start of either the prefix or suffix, depending on
422    /// which `error_kind` was set) that was invalid.
423    ///
424    /// However, if all characters are independently valid, but one side failed
425    /// to join (because the join itself would make the following character
426    /// invalid), then `byte_offset` will be set to `None`.
427    ///
428    /// [`Error`]: crate::Error
429    /// [`error_kind`]: crate::Error::error_kind
430    /// [`byte_offset`]: crate::Error::byte_offset
431    ///
432    /// # Examples
433    ///
434    /// Basic Usage:
435    ///
436    /// ```
437    /// # use typed_ident::presets::unicode::lower_snake::*;
438    /// let ident = LowerSnakeIdent::new("snake")?;
439    /// let ident = ident.with_circumfix_str("lower_", "_ident")?;
440    /// assert_eq!(ident.as_ref(), "lower_snake_ident");
441    /// # Ok::<(), typed_ident::Error>(())
442    /// ```
443    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
444    #[inline]
445    pub fn with_circumfix_str(
446        &self,
447        prefix: &str,
448        suffix: &str,
449    ) -> Result<Box<Ident<B, D, P>>, Error> {
450        self.with_circumfix(Fragment::new(prefix)?, Fragment::new(suffix)?)
451    }
452
453    /// Returns a heap-allocated identifier with the provided prefix attached to
454    /// the original identifier.
455    ///
456    /// It can be a bit cumbersome to use this function in most cases. Instead,
457    /// if you find it easier to work with string data (or you don't have any
458    /// fragments that you're joining with), you can use [`with_prefix_str`].
459    ///
460    /// [`with_prefix_str`]: Self::with_prefix_str
461    ///
462    /// # Errors
463    ///
464    /// Returns `Err` if the fragment formed from the combination of `prefix`
465    /// and `self` is invalid. If invalid, an [`Error`] is returned with the
466    /// [`error_kind`] set to `InvalidPrefix`.
467    ///
468    /// The value [`byte_offset`] will *NOT* be set from this function. None of
469    /// the individual characters are invalid, it's just that the combination of
470    /// joining the fragments themselves is invalid.
471    ///
472    /// [`Error`]: crate::Error
473    /// [`error_kind`]: crate::Error::error_kind
474    /// [`byte_offset`]: crate::Error::byte_offset
475    ///
476    /// # Examples
477    ///
478    /// Basic Usage:
479    ///
480    /// ```
481    /// # use typed_ident::presets::unicode::lower_snake::*;
482    /// let ident = LowerSnakeIdent::new("snake")?;
483    /// let ident = ident.with_prefix(
484    ///     LowerSnakeFragment::new("lower_")?,
485    /// )?;
486    /// assert_eq!(ident.as_ref(), "lower_snake");
487    /// # Ok::<(), typed_ident::Error>(())
488    /// ```
489    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
490    #[inline]
491    pub fn with_prefix(&self, prefix: &Fragment<B, D, P>) -> Result<Box<Ident<B, D, P>>, Error> {
492        let mut ident = IdentBuf::with_capacity(self.len() + prefix.len());
493        ident.push_fragment(prefix)?;
494        ident.push_fragment(self)?;
495        Ok(ident.into_boxed_ident().unwrap())
496    }
497
498    /// Returns a heap-allocated identifier with the provided prefix string
499    /// attached to the original identifier. The provided string is first
500    /// converted to a fragment before attempting to append it.
501    ///
502    /// # Errors
503    ///
504    /// Returns `Err` if the fragment formed from the combination of `prefix`
505    /// and `self` is invalid. If invalid, an [`Error`] is returned with the
506    /// [`error_kind`] set to `InvalidPrefix`.
507    ///
508    /// The value [`byte_offset`] *MAY* be set on this function. If the prefix
509    /// string contained invalid characters, this will be set to the byte index
510    /// (from the start of the prefix string) that was invalid.
511    ///
512    /// However, if all characters are independently valid, but one side failed
513    /// to join (because the join itself would make the following character
514    /// invalid), then `byte_offset` will be set to `None`.
515    ///
516    /// [`Error`]: crate::Error
517    /// [`error_kind`]: crate::Error::error_kind
518    /// [`byte_offset`]: crate::Error::byte_offset
519    ///
520    /// # Examples
521    ///
522    /// Basic Usage:
523    ///
524    /// ```
525    /// # use typed_ident::presets::unicode::lower_snake::*;
526    /// let ident = LowerSnakeIdent::new("snake")?;
527    /// let ident = ident.with_prefix_str("lower_")?;
528    /// assert_eq!(ident.as_ref(), "lower_snake");
529    /// # Ok::<(), typed_ident::Error>(())
530    /// ```
531    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
532    #[inline]
533    pub fn with_prefix_str(&self, prefix: &str) -> Result<Box<Ident<B, D, P>>, Error> {
534        self.with_prefix(Fragment::new(prefix)?)
535    }
536
537    /// Returns a heap-allocated identifier with the provided suffix attached to
538    /// the original identifier.
539    ///
540    /// It can be a bit cumbersome to use this function in most cases. Instead,
541    /// if you find it easier to work with string data (or you don't have any
542    /// fragments that you're joining with), you can use [`with_suffix_str`].
543    ///
544    /// [`with_suffix_str`]: Self::with_suffix_str
545    ///
546    /// # Errors
547    ///
548    /// Returns `Err` if the fragment formed from the combination of `self` and
549    /// `suffix` is invalid. If invalid, an [`Error`] is returned with the
550    /// [`error_kind`] set to `InvalidPrefix`.
551    ///
552    /// The value [`byte_offset`] will *NOT* be set from this function. None of
553    /// the individual characters are invalid, it's just that the combination of
554    /// joining the fragments themselves is invalid.
555    ///
556    /// [`Error`]: crate::Error
557    /// [`error_kind`]: crate::Error::error_kind
558    /// [`byte_offset`]: crate::Error::byte_offset
559    ///
560    /// # Examples
561    ///
562    /// Basic Usage:
563    ///
564    /// ```
565    /// # use typed_ident::presets::unicode::lower_snake::*;
566    /// let ident = LowerSnakeIdent::new("snake")?;
567    /// let ident = ident.with_suffix(
568    ///     LowerSnakeFragment::new("_ident")?,
569    /// )?;
570    /// assert_eq!(ident.as_ref(), "snake_ident");
571    /// # Ok::<(), typed_ident::Error>(())
572    /// ```
573    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
574    #[inline]
575    pub fn with_suffix(&self, suffix: &Fragment<B, D, P>) -> Result<Box<Ident<B, D, P>>, Error> {
576        let mut ident = IdentBuf::with_overhead(self, suffix.len());
577        ident.push_fragment(suffix)?;
578        Ok(ident.into_boxed_ident().unwrap())
579    }
580
581    /// Returns a heap-allocated identifier with the provided suffix string
582    /// attached to the original identifier. The provided string is first
583    /// converted to a fragment before attempting to append it.
584    ///
585    /// # Errors
586    ///
587    /// Returns `Err` if the fragment formed from the combination of `self` and
588    /// `suffix` is invalid. If invalid, an [`Error`] is returned with the
589    /// [`error_kind`] set to `InvalidPrefix`.
590    ///
591    /// The value [`byte_offset`] *MAY* be set on this function. If the suffix
592    /// string contained invalid characters, this will be set to the byte index
593    /// (from the start of the suffix string) that was invalid.
594    ///
595    /// However, if all characters are independently valid, but one side failed
596    /// to join (because the join itself would make the following character
597    /// invalid), then `byte_offset` will be set to `None`.
598    ///
599    /// [`Error`]: crate::Error
600    /// [`error_kind`]: crate::Error::error_kind
601    /// [`byte_offset`]: crate::Error::byte_offset
602    ///
603    /// # Examples
604    ///
605    /// Basic Usage:
606    ///
607    /// ```
608    /// # use typed_ident::presets::unicode::lower_snake::*;
609    /// let ident = LowerSnakeIdent::new("snake")?;
610    /// let ident = ident.with_suffix_str("_ident")?;
611    /// assert_eq!(ident.as_ref(), "snake_ident");
612    /// # Ok::<(), typed_ident::Error>(())
613    /// ```
614    #[must_use = "this function returns an allocated identifier, it does not mutate the original"]
615    #[inline]
616    pub fn with_suffix_str(&self, suffix: &str) -> Result<Box<Ident<B, D, P>>, Error> {
617        self.with_suffix(Fragment::new(suffix)?)
618    }
619}
620
621// -----------------------------------------------------------------------------
622impl<B, D, P> Ident<B, D, P> {
623    /// Converts a boxed identifier into a boxed string slice.
624    ///
625    /// # Examples
626    ///
627    /// Basic Usage:
628    ///
629    /// ```
630    /// # use typed_ident::*;
631    /// # use typed_ident::presets::unicode::lower_snake::*;
632    /// let ident: Box<LowerSnakeIdent> =
633    ///     Ident::new_boxed(String::from("snake_ident"))?;
634    /// let ident: Box<str> = ident.into_boxed_str();
635    /// # Ok::<(), typed_ident::Error>(())
636    /// ```
637    #[must_use]
638    #[inline]
639    pub fn into_boxed_str(self: Box<Ident<B, D, P>>) -> Box<str> {
640        // SAFETY: Ident is transparent over Fragment, which is itself
641        // transparent over str, so Box<Ident> has the same allocation layout,
642        // pointer metadata, alignment, and ownership behavior as Box<str>.
643        unsafe { Box::from_raw(Box::into_raw(self) as *mut str) }
644    }
645
646    /// Converts a boxed identifier into a fragment buffer.
647    ///
648    /// # Examples
649    ///
650    /// Basic Usage:
651    ///
652    /// ```
653    /// # use typed_ident::*;
654    /// # use typed_ident::presets::unicode::lower_snake::*;
655    /// let ident: Box<LowerSnakeIdent> =
656    ///     Ident::new_boxed(String::from("snake_ident"))?;
657    /// let buffer: LowerSnakeFragmentBuf = ident.into_fragment_buf();
658    /// # Ok::<(), typed_ident::Error>(())
659    /// ```
660    #[must_use]
661    #[inline]
662    pub fn into_fragment_buf(self: Box<Ident<B, D, P>>) -> FragmentBuf<B, D, P> {
663        FragmentBuf::from_string_unchecked(self.into_string())
664    }
665
666    /// Converts a boxed identifier into an identifier buffer.
667    ///
668    /// # Examples
669    ///
670    /// Basic Usage:
671    ///
672    /// ```
673    /// # use typed_ident::*;
674    /// # use typed_ident::presets::unicode::lower_snake::*;
675    /// let ident: Box<LowerSnakeIdent> =
676    ///     Ident::new_boxed(String::from("snake_ident"))?;
677    /// let buffer: LowerSnakeIdentBuf = ident.into_ident_buf();
678    /// # Ok::<(), typed_ident::Error>(())
679    /// ```
680    #[must_use]
681    #[inline]
682    pub fn into_ident_buf(self: Box<Ident<B, D, P>>) -> IdentBuf<B, D, P> {
683        IdentBuf::from_string_unchecked(self.into_string())
684    }
685
686    /// Converts a boxed identifier into a string.
687    ///
688    /// # Examples
689    ///
690    /// Basic Usage:
691    ///
692    /// ```
693    /// # use typed_ident::*;
694    /// # use typed_ident::presets::unicode::lower_snake::*;
695    /// let ident: Box<LowerSnakeIdent> =
696    ///     Ident::new_boxed(String::from("snake_ident"))?;
697    /// let ident: String = ident.into_string();
698    /// # Ok::<(), typed_ident::Error>(())
699    /// ```
700    #[must_use]
701    #[inline]
702    pub fn into_string(self: Box<Ident<B, D, P>>) -> String {
703        self.into_boxed_str().into()
704    }
705
706    /// Converts a string into a boxed identifier, bypassing checks.
707    #[must_use]
708    #[inline]
709    pub(crate) fn new_boxed_unchecked(string: String) -> Box<Ident<B, D, P>> {
710        let boxed_str = string.into_boxed_str();
711        // SAFETY: Ident is transparent over Fragment, which is itself
712        // transparent over str, so Box<Ident> has the same allocation layout,
713        // pointer metadata, alignment, and ownership behavior as Box<str>.
714        unsafe { Box::from_raw(Box::into_raw(boxed_str) as *mut Ident<B, D, P>) }
715    }
716
717    /// Converts an identifier into a fragment buffer.
718    ///
719    /// # Examples
720    ///
721    /// Basic Usage:
722    ///
723    /// ```
724    /// # use typed_ident::*;
725    /// # use typed_ident::presets::unicode::lower_snake::*;
726    /// let ident: &LowerSnakeIdent = Ident::new("snake_ident")?;
727    /// let buffer: LowerSnakeFragmentBuf = ident.to_fragment_buf();
728    /// # Ok::<(), typed_ident::Error>(())
729    /// ```
730    #[must_use]
731    #[inline]
732    pub fn to_fragment_buf(&self) -> FragmentBuf<B, D, P> {
733        FragmentBuf::from_fragment(self)
734    }
735
736    /// Converts an identifier into an identifier buffer.
737    ///
738    /// # Examples
739    ///
740    /// Basic Usage:
741    ///
742    /// ```
743    /// # use typed_ident::*;
744    /// # use typed_ident::presets::unicode::lower_snake::*;
745    /// let ident: &LowerSnakeIdent = Ident::new("snake_ident")?;
746    /// let buffer: LowerSnakeIdentBuf = ident.to_ident_buf();
747    /// # Ok::<(), typed_ident::Error>(())
748    /// ```
749    #[must_use]
750    #[inline]
751    pub fn to_ident_buf(&self) -> IdentBuf<B, D, P> {
752        IdentBuf::from_ident(self)
753    }
754}