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