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::{FromUtf8Error, FromUtf16Error, String};
10#[cfg(feature = "arbitrary")]
11use arbitrary::{Arbitrary, Unstructured};
12use core::fmt::{self, Debug, Display, Formatter, Write};
13use core::mem;
14use core::num::NonZeroUsize;
15use core::ops::{Deref, DerefMut, Index, IndexMut};
16use core::slice::SliceIndex;
17#[cfg(feature = "schemars")]
18use schemars::{JsonSchema, Schema, SchemaGenerator};
19
20use crate::borrow1::{CowStr1, CowStr1Ext as _};
21use crate::boxed1::{BoxedStr1, BoxedStr1Ext as _};
22use crate::iter1::{Extend1, FromIterator1, IntoIterator1};
23use crate::safety::{NonZeroExt as _, OptionExt as _};
24use crate::slice1::Slice1;
25use crate::str1::Str1;
26use crate::take;
27use crate::vec1::Vec1;
28use crate::{Cardinality, EmptyError, FromMaybeEmpty, MaybeEmpty, NonEmpty};
29
30impl<'a> Extend<&'a Str1> for String {
31    fn extend<I>(&mut self, items: I)
32    where
33        I: IntoIterator<Item = &'a Str1>,
34    {
35        self.extend(items.into_iter().map(Str1::as_str))
36    }
37}
38
39impl Extend<String1> for String {
40    fn extend<I>(&mut self, items: I)
41    where
42        I: IntoIterator<Item = String1>,
43    {
44        self.extend(items.into_iter().map(String1::into_string))
45    }
46}
47
48impl Extend1<char> for String {
49    fn extend_non_empty<I>(mut self, items: I) -> String1
50    where
51        I: IntoIterator1<Item = char>,
52    {
53        self.extend(items);
54        // SAFETY: The input iterator `items` is non-empty and `extend` either pushes one or more
55        //         items or panics, so `self` must be non-empty here.
56        unsafe { String1::from_maybe_empty_unchecked(self) }
57    }
58}
59
60unsafe impl MaybeEmpty for String {
61    fn cardinality(&self) -> Option<Cardinality<(), ()>> {
62        self.as_str().cardinality()
63    }
64}
65
66type TakeIfMany<'a, N = ()> = take::TakeIfMany<'a, String, char, N>;
67
68pub type PopIfMany<'a> = TakeIfMany<'a, ()>;
69
70pub type RemoveIfMany<'a> = TakeIfMany<'a, usize>;
71
72impl<N> TakeIfMany<'_, N> {
73    pub fn or_get_only(self) -> Result<char, char> {
74        self.take_or_else(|items, _| items.first())
75    }
76
77    pub fn or_replace_only(self, replacement: char) -> Result<char, char> {
78        self.or_else_replace_only(move || replacement)
79    }
80
81    pub fn or_else_replace_only<F>(self, f: F) -> Result<char, char>
82    where
83        F: FnOnce() -> char,
84    {
85        self.take_or_else(move |items, _| {
86            let target = items.first();
87            items.items.clear();
88            items.items.push(f());
89            target
90        })
91    }
92}
93
94impl TakeIfMany<'_, usize> {
95    pub fn or_get(self) -> Result<char, char> {
96        self.take_or_else(|items, index| {
97            if items.is_char_boundary(index) {
98                items.first()
99            }
100            else {
101                self::panic_index_is_not_char_boundary()
102            }
103        })
104    }
105
106    pub fn or_replace(self, replacement: char) -> Result<char, char> {
107        self.or_else_replace(move || replacement)
108    }
109
110    pub fn or_else_replace<F>(self, f: F) -> Result<char, char>
111    where
112        F: FnOnce() -> char,
113    {
114        self.take_or_else(move |items, index| {
115            if items.is_char_boundary(index) {
116                let target = items.items.remove(index);
117                items.items.push(f());
118                target
119            }
120            else {
121                self::panic_index_is_not_char_boundary()
122            }
123        })
124    }
125}
126
127pub type String1 = NonEmpty<String>;
128
129impl String1 {
130    /// # Safety
131    ///
132    /// `items` must be non-empty. For example, it is unsound to call this function with the
133    /// immediate output of [`Vec::new()`][`Vec::new`].
134    ///
135    /// [`Vec::new`]: alloc::vec::Vec::new
136    pub unsafe fn from_string_unchecked(items: String) -> Self {
137        unsafe { FromMaybeEmpty::from_maybe_empty_unchecked(items) }
138    }
139
140    pub fn from_one_with_capacity<U>(item: char, capacity: usize) -> Self {
141        String1::from_iter1_with_capacity([item], capacity)
142    }
143
144    pub fn from_iter1_with_capacity<U>(items: U, capacity: usize) -> Self
145    where
146        String: Extend1<U::Item>,
147        U: IntoIterator1,
148    {
149        String::with_capacity(capacity).extend_non_empty(items)
150    }
151
152    pub fn from_utf8(items: Vec1<u8>) -> Result<Self, FromUtf8Error> {
153        // SAFETY: `items` is non-empty and `String::from_utf8` checks for valid UTF-8, so there
154        //         must be one or more code points.
155        String::from_utf8(items.into_vec())
156            .map(|items| unsafe { String1::from_string_unchecked(items) })
157    }
158
159    pub fn from_utf8_lossy(items: &Slice1<u8>) -> CowStr1<'_> {
160        // SAFETY: `items` is non-empty and `String::from_utf8_lossy` checks for valid UTF-8 or
161        //         introduces replacement characters, so there must be one or more code points.
162        unsafe {
163            match String::from_utf8_lossy(items.as_slice()) {
164                Cow::Borrowed(items) => Cow::Borrowed(Str1::from_str_unchecked(items)),
165                Cow::Owned(items) => Cow::Owned(String1::from_string_unchecked(items)),
166            }
167        }
168    }
169
170    pub fn from_utf16(items: &Slice1<u16>) -> Result<Self, FromUtf16Error> {
171        // SAFETY: `items` is non-empty and `String::from_utf16` checks for valid UTF-16, so there
172        //         must be one or more code points.
173        String::from_utf16(items.as_slice())
174            .map(|items| unsafe { String1::from_string_unchecked(items) })
175    }
176
177    pub fn from_utf16_lossy(items: &Slice1<u16>) -> String1 {
178        // SAFETY: `items` is non-empty and `String::from_utf16_lossy` checks for valid UTF-16 or
179        //         introduces replacement characters, so there must be one or more code points.
180        unsafe { String1::from_string_unchecked(String::from_utf16_lossy(items.as_slice())) }
181    }
182
183    pub fn into_string(self) -> String {
184        self.items
185    }
186
187    pub fn into_boxed_str1(self) -> BoxedStr1 {
188        // SAFETY: `self` must be non-empty.
189        unsafe { BoxedStr1::from_boxed_str_unchecked(self.items.into_boxed_str()) }
190    }
191
192    pub fn leak<'a>(self) -> &'a mut Str1 {
193        // SAFETY: `self` must be non-empty.
194        unsafe { Str1::from_mut_str_unchecked(self.items.leak()) }
195    }
196
197    pub fn try_retain<F>(self, f: F) -> Result<Self, EmptyError<String>>
198    where
199        F: FnMut(char) -> bool,
200    {
201        self.and_then_try(|items| items.retain(f))
202    }
203
204    pub fn reserve(&mut self, additional: usize) {
205        self.items.reserve(additional)
206    }
207
208    pub fn reserve_exact(&mut self, additional: usize) {
209        self.items.reserve_exact(additional)
210    }
211
212    pub fn shrink_to(&mut self, capacity: usize) {
213        self.items.shrink_to(capacity)
214    }
215
216    pub fn shrink_to_fit(&mut self) {
217        self.items.shrink_to_fit()
218    }
219
220    pub fn split_off_tail(&mut self) -> String {
221        let index = unsafe {
222            // SAFETY: `self` must be non-empty.
223            self.items
224                .char_indices()
225                .take(2)
226                .last()
227                .map(|(index, _)| index)
228                .unwrap_maybe_unchecked()
229        };
230        self.items.split_off(index)
231    }
232
233    pub fn push(&mut self, item: char) {
234        self.items.push(item)
235    }
236
237    pub fn push_str(&mut self, items: &str) {
238        self.items.push_str(items)
239    }
240
241    pub fn pop_if_many(&mut self) -> PopIfMany<'_> {
242        // SAFETY: `with` executes this closure only if `self` contains more than one item.
243        TakeIfMany::with(self, (), |items, ()| unsafe {
244            items.items.pop().unwrap_maybe_unchecked()
245        })
246    }
247
248    pub fn insert(&mut self, index: usize, item: char) {
249        self.items.insert(index, item)
250    }
251
252    pub fn remove_if_many(&mut self, index: usize) -> RemoveIfMany<'_> {
253        TakeIfMany::with(self, index, |items, index| items.items.remove(index))
254    }
255
256    pub fn len(&self) -> NonZeroUsize {
257        // SAFETY: `self` must be non-empty.
258        unsafe { NonZeroUsize::new_maybe_unchecked(self.items.len()) }
259    }
260
261    pub fn capacity(&self) -> NonZeroUsize {
262        // SAFETY: `self` must be non-empty.
263        unsafe { NonZeroUsize::new_maybe_unchecked(self.items.capacity()) }
264    }
265
266    pub const fn as_string(&self) -> &String {
267        &self.items
268    }
269
270    /// # Safety
271    ///
272    /// The [`String`] behind the returned mutable reference **must not** be empty when the
273    /// reference is dropped. Consider the following example:
274    ///
275    /// ```rust,no_run
276    /// use mitsein::string1::String1;
277    ///
278    /// let mut xs = String1::try_from("abc").unwrap();
279    /// // This block is unsound. The `&mut String` is dropped in the block and so `xs` can be
280    /// // freely manipulated after the block despite violation of the non-empty guarantee.
281    /// unsafe {
282    ///     xs.as_mut_string().clear();
283    /// }
284    /// let x = xs.as_bytes1().first(); // Undefined behavior!
285    /// ```
286    pub const unsafe fn as_mut_string(&mut self) -> &mut String {
287        &mut self.items
288    }
289
290    /// # Safety
291    ///
292    /// The returned [`Vec1`] must contain valid UTF-8 when the reference is dropped. Note that the
293    /// non-empty guarantee of `String1` may also be violated by invalid UTF-8, because invalid
294    /// UTF-8 bytes may yield no code points.
295    pub unsafe fn as_mut_vec1(&mut self) -> &mut Vec1<u8> {
296        unsafe { mem::transmute(self.items.as_mut_vec()) }
297    }
298
299    pub fn as_str1(&self) -> &Str1 {
300        // SAFETY: `self` must be non-empty.
301        unsafe { Str1::from_str_unchecked(self.items.as_str()) }
302    }
303
304    pub fn as_mut_str1(&mut self) -> &mut Str1 {
305        // SAFETY: `self` must be non-empty.
306        unsafe { Str1::from_mut_str_unchecked(self.items.as_mut_str()) }
307    }
308
309    pub fn as_ptr(&self) -> *const u8 {
310        self.items.as_ptr()
311    }
312
313    pub fn as_mut_ptr(&mut self) -> *mut u8 {
314        self.items.as_mut_ptr()
315    }
316}
317
318#[cfg(feature = "arbitrary")]
319#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
320impl<'a> Arbitrary<'a> for String1 {
321    fn arbitrary(unstructured: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
322        <&'a Str1>::arbitrary(unstructured).map(String1::from)
323    }
324
325    fn size_hint(depth: usize) -> (usize, Option<usize>) {
326        (<&'a Str1>::size_hint(depth).0, None)
327    }
328}
329
330impl AsMut<str> for String1 {
331    fn as_mut(&mut self) -> &mut str {
332        self.items.as_mut()
333    }
334}
335
336impl AsMut<Str1> for String1 {
337    fn as_mut(&mut self) -> &mut Str1 {
338        self.as_mut_str1()
339    }
340}
341
342impl AsRef<str> for String1 {
343    fn as_ref(&self) -> &str {
344        self.items.as_ref()
345    }
346}
347
348impl AsRef<Str1> for String1 {
349    fn as_ref(&self) -> &Str1 {
350        self.as_str1()
351    }
352}
353
354impl Borrow<str> for String1 {
355    fn borrow(&self) -> &str {
356        self.items.borrow()
357    }
358}
359
360impl Borrow<Str1> for String1 {
361    fn borrow(&self) -> &Str1 {
362        self.as_str1()
363    }
364}
365
366impl BorrowMut<str> for String1 {
367    fn borrow_mut(&mut self) -> &mut str {
368        self.items.borrow_mut()
369    }
370}
371
372impl BorrowMut<Str1> for String1 {
373    fn borrow_mut(&mut self) -> &mut Str1 {
374        self.as_mut_str1()
375    }
376}
377
378impl Debug for String1 {
379    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
380        write!(formatter, "{:?}", &self.items)
381    }
382}
383
384impl Deref for String1 {
385    type Target = Str1;
386
387    fn deref(&self) -> &Self::Target {
388        self.as_str1()
389    }
390}
391
392impl DerefMut for String1 {
393    fn deref_mut(&mut self) -> &mut Self::Target {
394        self.as_mut_str1()
395    }
396}
397
398impl Display for String1 {
399    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
400        write!(formatter, "{}", &self.items)
401    }
402}
403
404// This unfortunately cannot support extending from `CowStr1`s, because `Extend<CowStr1<'_>>`
405// cannot be implemented for `String` in this crate. It cannot be implemented directly for
406// `String1` either, because it conflicts with this implementation.
407impl<T> Extend<T> for String1
408where
409    String: Extend<T>,
410{
411    fn extend<I>(&mut self, extension: I)
412    where
413        I: IntoIterator<Item = T>,
414    {
415        self.items.extend(extension)
416    }
417}
418
419impl From<BoxedStr1> for String1 {
420    fn from(items: BoxedStr1) -> Self {
421        // SAFETY: `items` must be non-empty.
422        unsafe { String1::from_string_unchecked(String::from(items.into_boxed_str())) }
423    }
424}
425
426impl From<char> for String1 {
427    fn from(point: char) -> Self {
428        // SAFETY: The `From<char>` implementation for `String` never constructs an empty `String`.
429        unsafe { String1::from_string_unchecked(String::from(point)) }
430    }
431}
432
433impl<'a> From<CowStr1<'a>> for String1 {
434    fn from(items: CowStr1<'a>) -> Self {
435        items.into_owned()
436    }
437}
438
439impl<'a> From<&'a Str1> for String1 {
440    fn from(items: &'a Str1) -> Self {
441        // SAFETY: `items` must be non-empty.
442        unsafe { String1::from_string_unchecked(String::from(items.as_str())) }
443    }
444}
445
446impl<'a> From<&'a mut Str1> for String1 {
447    fn from(items: &'a mut Str1) -> Self {
448        // SAFETY: `items` must be non-empty.
449        unsafe { String1::from_string_unchecked(String::from(items.as_str())) }
450    }
451}
452
453impl From<String1> for String {
454    fn from(items: String1) -> Self {
455        items.items
456    }
457}
458
459impl FromIterator1<char> for String1 {
460    fn from_iter1<I>(items: I) -> Self
461    where
462        I: IntoIterator1<Item = char>,
463    {
464        // SAFETY: `items` is non-empty and each item (`char`) is intrinsically non-empty. A
465        //         `String` constructed from one or more `char`s is never empty.
466        unsafe { String1::from_string_unchecked(items.into_iter().collect()) }
467    }
468}
469
470impl<'a> FromIterator1<&'a char> for String1 {
471    fn from_iter1<I>(items: I) -> Self
472    where
473        I: IntoIterator1<Item = &'a char>,
474    {
475        String1::from_iter1(items.into_iter1().cloned())
476    }
477}
478
479impl<'a> FromIterator1<CowStr1<'a>> for String1 {
480    fn from_iter1<I>(items: I) -> Self
481    where
482        I: IntoIterator1<Item = CowStr1<'a>>,
483    {
484        let (head, tail) = items.into_iter1().into_head_and_tail();
485        let mut head = head.into_owned();
486        head.items.extend(tail.map(CowStr1::into_cow_str));
487        head
488    }
489}
490
491impl<'a> FromIterator1<&'a Str1> for String1 {
492    fn from_iter1<I>(items: I) -> Self
493    where
494        I: IntoIterator1<Item = &'a Str1>,
495    {
496        let (head, tail) = items.into_iter1().into_head_and_tail();
497        let mut head = String1::from(head);
498        head.extend(tail);
499        head
500    }
501}
502
503impl FromIterator1<String1> for String1 {
504    fn from_iter1<I>(items: I) -> Self
505    where
506        I: IntoIterator1<Item = String1>,
507    {
508        let (mut head, tail) = items.into_iter1().into_head_and_tail();
509        head.extend(tail);
510        head
511    }
512}
513
514impl<I> Index<I> for String1
515where
516    I: SliceIndex<str>,
517{
518    type Output = I::Output;
519
520    fn index(&self, at: I) -> &Self::Output {
521        self.items.index(at)
522    }
523}
524
525impl<I> IndexMut<I> for String1
526where
527    I: SliceIndex<str>,
528{
529    fn index_mut(&mut self, at: I) -> &mut Self::Output {
530        self.items.index_mut(at)
531    }
532}
533
534#[cfg(feature = "schemars")]
535#[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
536impl JsonSchema for String1 {
537    fn schema_name() -> Cow<'static, str> {
538        String::schema_name()
539    }
540
541    fn json_schema(generator: &mut SchemaGenerator) -> Schema {
542        use crate::schemars;
543
544        schemars::json_subschema_with_non_empty_property_for::<String>(
545            schemars::NON_EMPTY_KEY_STRING,
546            generator,
547        )
548    }
549
550    fn inline_schema() -> bool {
551        String::inline_schema()
552    }
553
554    fn schema_id() -> Cow<'static, str> {
555        String::schema_id()
556    }
557}
558
559crate::impl_partial_eq_for_non_empty!([in str] <= [in String1]);
560crate::impl_partial_eq_for_non_empty!([in &str] <= [in String1]);
561crate::impl_partial_eq_for_non_empty!([in &Str1] == [in String1]);
562crate::impl_partial_eq_for_non_empty!([in CowStr1<'_>] == [in String1]);
563crate::impl_partial_eq_for_non_empty!([in String1] => [in str]);
564crate::impl_partial_eq_for_non_empty!([in String1] => [in &str]);
565crate::impl_partial_eq_for_non_empty!([in String1] == [in &Str1]);
566
567impl<'a> TryFrom<&'a str> for String1 {
568    type Error = EmptyError<&'a str>;
569
570    fn try_from(items: &'a str) -> Result<Self, Self::Error> {
571        Str1::try_from_str(items).map(String1::from)
572    }
573}
574
575impl<'a> TryFrom<&'a mut str> for String1 {
576    type Error = EmptyError<&'a mut str>;
577
578    fn try_from(items: &'a mut str) -> Result<Self, Self::Error> {
579        Str1::try_from_mut_str(items).map(String1::from)
580    }
581}
582
583impl TryFrom<String> for String1 {
584    type Error = EmptyError<String>;
585
586    fn try_from(items: String) -> Result<Self, Self::Error> {
587        FromMaybeEmpty::try_from_maybe_empty(items)
588    }
589}
590
591impl TryFrom<Vec1<u8>> for String1 {
592    type Error = FromUtf8Error;
593
594    fn try_from(items: Vec1<u8>) -> Result<Self, Self::Error> {
595        String1::from_utf8(items)
596    }
597}
598
599impl Write for String1 {
600    fn write_str(&mut self, items: &str) -> fmt::Result {
601        self.items.write_str(items)
602    }
603
604    fn write_char(&mut self, item: char) -> fmt::Result {
605        self.items.write_char(item)
606    }
607}
608
609const fn panic_index_is_not_char_boundary() -> ! {
610    panic!("index is not at a UTF-8 code point boundary")
611}
612
613#[cfg(test)]
614pub mod harness {
615    use rstest::fixture;
616
617    use crate::iter1;
618    use crate::string1::String1;
619
620    #[fixture]
621    pub fn xs1(#[default(4)] end: u8) -> String1 {
622        iter1::one('x')
623            .first_and_then_take(usize::from(end))
624            .collect1()
625    }
626}
627
628#[cfg(all(test, feature = "schemars"))]
629mod tests {
630    use rstest::rstest;
631
632    use crate::schemars;
633    use crate::string1::String1;
634
635    #[rstest]
636    fn string1_json_schema_has_non_empty_property() {
637        schemars::harness::assert_json_schema_has_non_empty_property::<String1>(
638            schemars::NON_EMPTY_KEY_STRING,
639        );
640    }
641}