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}