mitsein/
string1.rs

1//! A non-empty [`String`][`string`].
2//!
3//! [`string`]: alloc::string
4
5#![cfg(feature = "alloc")]
6#![cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
7
8use alloc::borrow::{Borrow, BorrowMut, Cow};
9use alloc::string::{FromUtf16Error, FromUtf8Error, String};
10#[cfg(feature = "arbitrary")]
11use arbitrary::{Arbitrary, Unstructured};
12use core::fmt::{self, Debug, Formatter, Write};
13use core::mem;
14use core::num::NonZeroUsize;
15use core::ops::{Deref, DerefMut, Index, IndexMut};
16use core::slice::SliceIndex;
17
18use crate::borrow1::CowStr1;
19use crate::boxed1::{BoxedStr1, BoxedStr1Ext as _};
20use crate::iter1::{Extend1, FromIterator1, IntoIterator1};
21use crate::safety::{NonZeroExt as _, OptionExt as _};
22use crate::slice1::Slice1;
23use crate::str1::Str1;
24use crate::take;
25use crate::vec1::Vec1;
26use crate::{Cardinality, FromMaybeEmpty, MaybeEmpty, NonEmpty};
27
28impl Extend1<char> for String {
29    fn extend_non_empty<I>(mut self, items: I) -> String1
30    where
31        I: IntoIterator1<Item = char>,
32    {
33        self.extend(items);
34        // SAFETY: The input iterator `items` is non-empty and `extend` either pushes one or more
35        //         items or panics, so `self` must be non-empty here.
36        unsafe { String1::from_maybe_empty_unchecked(self) }
37    }
38}
39
40unsafe impl MaybeEmpty for String {
41    fn cardinality(&self) -> Option<Cardinality<(), ()>> {
42        self.as_str().cardinality()
43    }
44}
45
46type TakeOr<'a, N = ()> = take::TakeOr<'a, String, char, N>;
47
48pub type PopOr<'a> = TakeOr<'a, ()>;
49
50pub type RemoveOr<'a> = TakeOr<'a, usize>;
51
52impl<N> TakeOr<'_, N> {
53    pub fn get_only(self) -> Result<char, char> {
54        self.take_or_else(|items, _| items.first())
55    }
56
57    pub fn replace_only(self, replacement: char) -> Result<char, char> {
58        self.else_replace_only(move || replacement)
59    }
60
61    pub fn else_replace_only<F>(self, f: F) -> Result<char, char>
62    where
63        F: FnOnce() -> char,
64    {
65        self.take_or_else(move |items, _| {
66            let target = items.first();
67            items.items.clear();
68            items.items.push(f());
69            target
70        })
71    }
72}
73
74impl TakeOr<'_, usize> {
75    pub fn get(self) -> Result<char, char> {
76        self.take_or_else(|items, index| {
77            if items.is_char_boundary(index) {
78                items.first()
79            }
80            else {
81                self::panic_index_is_not_char_boundary()
82            }
83        })
84    }
85
86    pub fn replace(self, replacement: char) -> Result<char, char> {
87        self.else_replace(move || replacement)
88    }
89
90    pub fn else_replace<F>(self, f: F) -> Result<char, char>
91    where
92        F: FnOnce() -> char,
93    {
94        self.take_or_else(move |items, index| {
95            if items.is_char_boundary(index) {
96                let target = items.items.remove(index);
97                items.items.push(f());
98                target
99            }
100            else {
101                self::panic_index_is_not_char_boundary()
102            }
103        })
104    }
105}
106
107pub type String1 = NonEmpty<String>;
108
109impl String1 {
110    /// # Safety
111    ///
112    /// `items` must be non-empty. For example, it is unsound to call this function with the
113    /// immediate output of [`Vec::new()`][`Vec::new`].
114    ///
115    /// [`Vec::new`]: alloc::vec::Vec::new
116    pub unsafe fn from_string_unchecked(items: String) -> Self {
117        FromMaybeEmpty::from_maybe_empty_unchecked(items)
118    }
119
120    pub fn from_one_with_capacity<U>(item: char, capacity: usize) -> Self {
121        String1::from_iter1_with_capacity([item], capacity)
122    }
123
124    pub fn from_iter1_with_capacity<U>(items: U, capacity: usize) -> Self
125    where
126        String: Extend1<U::Item>,
127        U: IntoIterator1,
128    {
129        String::with_capacity(capacity).extend_non_empty(items)
130    }
131
132    pub fn from_utf8(items: Vec1<u8>) -> Result<Self, FromUtf8Error> {
133        // SAFETY: `items` is non-empty and `String::from_utf8` checks for valid UTF-8, so there
134        //         must be one or more code points.
135        String::from_utf8(items.into_vec())
136            .map(|items| unsafe { String1::from_string_unchecked(items) })
137    }
138
139    pub fn from_utf8_lossy(items: &Slice1<u8>) -> CowStr1 {
140        // SAFETY: `items` is non-empty and `String::from_utf8_lossy` checks for valid UTF-8 or
141        //         introduces replacement characters, so there must be one or more code points.
142        unsafe {
143            match String::from_utf8_lossy(items.as_slice()) {
144                Cow::Borrowed(items) => Cow::Borrowed(Str1::from_str_unchecked(items)),
145                Cow::Owned(items) => Cow::Owned(String1::from_string_unchecked(items)),
146            }
147        }
148    }
149
150    pub fn from_utf16(items: &Slice1<u16>) -> Result<Self, FromUtf16Error> {
151        // SAFETY: `items` is non-empty and `String::from_utf16` checks for valid UTF-16, so there
152        //         must be one or more code points.
153        String::from_utf16(items.as_slice())
154            .map(|items| unsafe { String1::from_string_unchecked(items) })
155    }
156
157    pub fn from_utf16_lossy(items: &Slice1<u16>) -> String1 {
158        // SAFETY: `items` is non-empty and `String::from_utf16` checks for valid UTF-16, so there
159        //         must be one or more code points.
160        unsafe { String1::from_string_unchecked(String::from_utf16_lossy(items.as_slice())) }
161    }
162
163    pub fn into_string(self) -> String {
164        self.items
165    }
166
167    pub fn into_boxed_str1(self) -> BoxedStr1 {
168        // SAFETY: `self` must be non-empty.
169        unsafe { BoxedStr1::from_boxed_str_unchecked(self.items.into_boxed_str()) }
170    }
171
172    pub fn leak<'a>(self) -> &'a mut Str1 {
173        // SAFETY: `self` must be non-empty.
174        unsafe { Str1::from_mut_str_unchecked(self.items.leak()) }
175    }
176
177    pub fn reserve(&mut self, additional: usize) {
178        self.items.reserve(additional)
179    }
180
181    pub fn reserve_exact(&mut self, additional: usize) {
182        self.items.reserve_exact(additional)
183    }
184
185    pub fn shrink_to(&mut self, capacity: usize) {
186        self.items.shrink_to(capacity)
187    }
188
189    pub fn shrink_to_fit(&mut self) {
190        self.items.shrink_to_fit()
191    }
192
193    pub fn split_off_tail(&mut self) -> String {
194        let index = unsafe {
195            // SAFETY: `self` must be non-empty.
196            self.items
197                .char_indices()
198                .take(2)
199                .last()
200                .map(|(index, _)| index)
201                .unwrap_maybe_unchecked()
202        };
203        self.items.split_off(index)
204    }
205
206    pub fn push(&mut self, item: char) {
207        self.items.push(item)
208    }
209
210    pub fn push_str(&mut self, items: &str) {
211        self.items.push_str(items)
212    }
213
214    pub fn pop_or(&mut self) -> PopOr<'_> {
215        // SAFETY: `with` executes this closure only if `self` contains more than one item.
216        TakeOr::with(self, (), |items, ()| unsafe {
217            items.items.pop().unwrap_maybe_unchecked()
218        })
219    }
220
221    pub fn insert(&mut self, index: usize, item: char) {
222        self.items.insert(index, item)
223    }
224
225    pub fn remove_or(&mut self, index: usize) -> RemoveOr<'_> {
226        TakeOr::with(self, index, |items, index| items.items.remove(index))
227    }
228
229    pub fn len(&self) -> NonZeroUsize {
230        // SAFETY: `self` must be non-empty.
231        unsafe { NonZeroUsize::new_maybe_unchecked(self.items.len()) }
232    }
233
234    pub fn capacity(&self) -> NonZeroUsize {
235        // SAFETY: `self` must be non-empty.
236        unsafe { NonZeroUsize::new_maybe_unchecked(self.items.capacity()) }
237    }
238
239    pub const fn as_string(&self) -> &String {
240        &self.items
241    }
242
243    /// # Safety
244    ///
245    /// The returned [`Vec1`] must contain valid UTF-8 when the reference is dropped. Note that the
246    /// non-empty guarantee of `String1` may also be violated by invalid UTF-8, because invalid
247    /// UTF-8 bytes may yield no code points.
248    pub unsafe fn as_mut_vec1(&mut self) -> &mut Vec1<u8> {
249        unsafe { mem::transmute(self.items.as_mut_vec()) }
250    }
251
252    pub fn as_str1(&self) -> &Str1 {
253        // SAFETY: `self` must be non-empty.
254        unsafe { Str1::from_str_unchecked(self.items.as_str()) }
255    }
256
257    pub fn as_mut_str1(&mut self) -> &mut Str1 {
258        // SAFETY: `self` must be non-empty.
259        unsafe { Str1::from_mut_str_unchecked(self.items.as_mut_str()) }
260    }
261
262    pub fn as_ptr(&self) -> *const u8 {
263        self.items.as_ptr()
264    }
265
266    pub fn as_mut_ptr(&mut self) -> *mut u8 {
267        self.items.as_mut_ptr()
268    }
269}
270
271#[cfg(feature = "arbitrary")]
272#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
273impl<'a> Arbitrary<'a> for String1 {
274    fn arbitrary(unstructured: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
275        <&'a Str1>::arbitrary(unstructured).map(String1::from)
276    }
277
278    fn size_hint(depth: usize) -> (usize, Option<usize>) {
279        (<&'a Str1>::size_hint(depth).0, None)
280    }
281}
282
283impl AsMut<str> for String1 {
284    fn as_mut(&mut self) -> &mut str {
285        self.items.as_mut()
286    }
287}
288
289impl AsMut<Str1> for String1 {
290    fn as_mut(&mut self) -> &mut Str1 {
291        self.as_mut_str1()
292    }
293}
294
295impl AsRef<str> for String1 {
296    fn as_ref(&self) -> &str {
297        self.items.as_ref()
298    }
299}
300
301impl AsRef<Str1> for String1 {
302    fn as_ref(&self) -> &Str1 {
303        self.as_str1()
304    }
305}
306
307impl Borrow<str> for String1 {
308    fn borrow(&self) -> &str {
309        self.items.borrow()
310    }
311}
312
313impl Borrow<Str1> for String1 {
314    fn borrow(&self) -> &Str1 {
315        self.as_str1()
316    }
317}
318
319impl BorrowMut<str> for String1 {
320    fn borrow_mut(&mut self) -> &mut str {
321        self.items.borrow_mut()
322    }
323}
324
325impl BorrowMut<Str1> for String1 {
326    fn borrow_mut(&mut self) -> &mut Str1 {
327        self.as_mut_str1()
328    }
329}
330
331impl Debug for String1 {
332    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
333        write!(formatter, "{:?}", &self.items)
334    }
335}
336
337impl Deref for String1 {
338    type Target = Str1;
339
340    fn deref(&self) -> &Self::Target {
341        self.as_str1()
342    }
343}
344
345impl DerefMut for String1 {
346    fn deref_mut(&mut self) -> &mut Self::Target {
347        self.as_mut_str1()
348    }
349}
350
351impl<T> Extend<T> for String1
352where
353    String: Extend<T>,
354{
355    fn extend<I>(&mut self, extension: I)
356    where
357        I: IntoIterator<Item = T>,
358    {
359        self.items.extend(extension)
360    }
361}
362
363impl From<BoxedStr1> for String1 {
364    fn from(items: BoxedStr1) -> Self {
365        // SAFETY: `items` must be non-empty.
366        unsafe { String1::from_string_unchecked(String::from(items.into_boxed_str())) }
367    }
368}
369
370impl<'a> From<CowStr1<'a>> for String1 {
371    fn from(items: CowStr1<'a>) -> Self {
372        items.into_owned()
373    }
374}
375
376impl<'a> From<&'a Str1> for String1 {
377    fn from(items: &'a Str1) -> Self {
378        // SAFETY: `items` must be non-empty.
379        unsafe { String1::from_string_unchecked(String::from(items.as_str())) }
380    }
381}
382
383impl<'a> From<&'a mut Str1> for String1 {
384    fn from(items: &'a mut Str1) -> Self {
385        // SAFETY: `items` must be non-empty.
386        unsafe { String1::from_string_unchecked(String::from(items.as_str())) }
387    }
388}
389
390impl From<String1> for String {
391    fn from(items: String1) -> Self {
392        items.items
393    }
394}
395
396impl FromIterator1<char> for String1 {
397    fn from_iter1<I>(items: I) -> Self
398    where
399        I: IntoIterator1<Item = char>,
400    {
401        // SAFETY: `items` must be non-empty.
402        unsafe { String1::from_string_unchecked(items.into_iter().collect()) }
403    }
404}
405
406impl<I> Index<I> for String1
407where
408    I: SliceIndex<str>,
409{
410    type Output = I::Output;
411
412    fn index(&self, at: I) -> &Self::Output {
413        self.items.index(at)
414    }
415}
416
417impl<I> IndexMut<I> for String1
418where
419    I: SliceIndex<str>,
420{
421    fn index_mut(&mut self, at: I) -> &mut Self::Output {
422        self.items.index_mut(at)
423    }
424}
425
426impl<'a> TryFrom<&'a str> for String1 {
427    type Error = &'a str;
428
429    fn try_from(items: &'a str) -> Result<Self, Self::Error> {
430        Str1::try_from_str(items).map(String1::from)
431    }
432}
433
434impl<'a> TryFrom<&'a mut str> for String1 {
435    type Error = &'a mut str;
436
437    fn try_from(items: &'a mut str) -> Result<Self, Self::Error> {
438        Str1::try_from_mut_str(items).map(String1::from)
439    }
440}
441
442impl TryFrom<String> for String1 {
443    type Error = String;
444
445    fn try_from(items: String) -> Result<Self, Self::Error> {
446        FromMaybeEmpty::try_from_maybe_empty(items)
447    }
448}
449
450impl Write for String1 {
451    fn write_str(&mut self, items: &str) -> fmt::Result {
452        self.items.write_str(items)
453    }
454
455    fn write_char(&mut self, item: char) -> fmt::Result {
456        self.items.write_char(item)
457    }
458}
459
460const fn panic_index_is_not_char_boundary() -> ! {
461    panic!("index is not at a UTF-8 code point boundary")
462}
463
464#[cfg(test)]
465pub mod harness {
466    use rstest::fixture;
467
468    use crate::iter1;
469    use crate::string1::String1;
470
471    #[fixture]
472    pub fn xs1(#[default(4)] end: u8) -> String1 {
473        iter1::one('x')
474            .first_and_then_take(usize::from(end))
475            .collect1()
476    }
477}
478
479#[cfg(test)]
480mod tests {}