glib/collections/
strv.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{ffi::c_char, fmt, marker::PhantomData, mem, ptr};
4
5use crate::{ffi, gobject_ffi, prelude::*, translate::*, GStr, GString, GStringPtr};
6
7// rustdoc-stripper-ignore-next
8/// Minimum size of the `StrV` allocation.
9const MIN_SIZE: usize = 16;
10
11// rustdoc-stripper-ignore-next
12/// `NULL`-terminated array of `NULL`-terminated strings.
13///
14/// The underlying memory is always `NULL`-terminated.
15///
16/// This can be used like a `&[&str]`, `&mut [&str]` and `Vec<&str>`.
17pub struct StrV {
18    ptr: ptr::NonNull<*mut c_char>,
19    // rustdoc-stripper-ignore-next
20    /// Length without the `NULL`-terminator.
21    len: usize,
22    // rustdoc-stripper-ignore-next
23    /// Capacity **with** the `NULL`-terminator, i.e. the actual allocation size.
24    capacity: usize,
25}
26
27impl fmt::Debug for StrV {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        self.as_slice().fmt(f)
30    }
31}
32
33unsafe impl Send for StrV {}
34
35unsafe impl Sync for StrV {}
36
37impl PartialEq for StrV {
38    #[inline]
39    fn eq(&self, other: &Self) -> bool {
40        self.as_slice() == other.as_slice()
41    }
42}
43
44impl Eq for StrV {}
45
46impl PartialOrd for StrV {
47    #[inline]
48    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
49        Some(self.cmp(other))
50    }
51}
52
53impl Ord for StrV {
54    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
55        self.as_slice().cmp(other.as_slice())
56    }
57}
58
59impl std::hash::Hash for StrV {
60    #[inline]
61    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
62        self.as_slice().hash(state)
63    }
64}
65
66impl PartialEq<[&'_ str]> for StrV {
67    fn eq(&self, other: &[&'_ str]) -> bool {
68        for (a, b) in Iterator::zip(self.iter(), other.iter()) {
69            if a != b {
70                return false;
71            }
72        }
73
74        true
75    }
76}
77
78impl PartialEq<StrV> for [&'_ str] {
79    #[inline]
80    fn eq(&self, other: &StrV) -> bool {
81        other.eq(self)
82    }
83}
84
85impl Drop for StrV {
86    #[inline]
87    fn drop(&mut self) {
88        unsafe {
89            if self.capacity != 0 {
90                ffi::g_strfreev(self.ptr.as_ptr());
91            }
92        }
93    }
94}
95
96impl Default for StrV {
97    #[inline]
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103impl AsRef<[GStringPtr]> for StrV {
104    #[inline]
105    fn as_ref(&self) -> &[GStringPtr] {
106        self.as_slice()
107    }
108}
109
110impl std::borrow::Borrow<[GStringPtr]> for StrV {
111    #[inline]
112    fn borrow(&self) -> &[GStringPtr] {
113        self.as_slice()
114    }
115}
116
117impl std::ops::Deref for StrV {
118    type Target = [GStringPtr];
119
120    #[inline]
121    fn deref(&self) -> &[GStringPtr] {
122        self.as_slice()
123    }
124}
125
126impl std::iter::Extend<GString> for StrV {
127    #[inline]
128    fn extend<I: IntoIterator<Item = GString>>(&mut self, iter: I) {
129        let iter = iter.into_iter();
130        self.reserve(iter.size_hint().0);
131
132        for item in iter {
133            self.push(item);
134        }
135    }
136}
137
138impl<'a> std::iter::Extend<&'a str> for StrV {
139    #[inline]
140    fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
141        let iter = iter.into_iter();
142        self.reserve(iter.size_hint().0);
143
144        for item in iter {
145            self.push(GString::from(item));
146        }
147    }
148}
149
150impl std::iter::FromIterator<GString> for StrV {
151    #[inline]
152    fn from_iter<I: IntoIterator<Item = GString>>(iter: I) -> Self {
153        let iter = iter.into_iter();
154        let mut s = Self::with_capacity(iter.size_hint().0);
155        for item in iter {
156            s.push(item);
157        }
158        s
159    }
160}
161
162impl<'a> std::iter::IntoIterator for &'a StrV {
163    type Item = &'a GStringPtr;
164    type IntoIter = std::slice::Iter<'a, GStringPtr>;
165
166    #[inline]
167    fn into_iter(self) -> Self::IntoIter {
168        self.as_slice().iter()
169    }
170}
171
172impl std::iter::IntoIterator for StrV {
173    type Item = GString;
174    type IntoIter = IntoIter;
175
176    #[inline]
177    fn into_iter(self) -> Self::IntoIter {
178        IntoIter::new(self)
179    }
180}
181
182pub struct IntoIter {
183    ptr: ptr::NonNull<*mut c_char>,
184    idx: ptr::NonNull<*mut c_char>,
185    len: usize,
186    empty: bool,
187}
188
189impl IntoIter {
190    #[inline]
191    fn new(slice: StrV) -> Self {
192        let slice = mem::ManuallyDrop::new(slice);
193        IntoIter {
194            ptr: slice.ptr,
195            idx: slice.ptr,
196            len: slice.len,
197            empty: slice.capacity == 0,
198        }
199    }
200
201    // rustdoc-stripper-ignore-next
202    /// Returns the remaining items as slice.
203    #[inline]
204    pub const fn as_slice(&self) -> &[GStringPtr] {
205        unsafe {
206            if self.len == 0 {
207                &[]
208            } else {
209                std::slice::from_raw_parts(self.idx.as_ptr() as *const GStringPtr, self.len)
210            }
211        }
212    }
213}
214
215impl Drop for IntoIter {
216    #[inline]
217    fn drop(&mut self) {
218        unsafe {
219            for i in 0..self.len {
220                ffi::g_free(*self.idx.as_ptr().add(i) as ffi::gpointer);
221            }
222
223            if !self.empty {
224                ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
225            }
226        }
227    }
228}
229
230impl Iterator for IntoIter {
231    type Item = GString;
232
233    #[inline]
234    fn next(&mut self) -> Option<Self::Item> {
235        if self.len == 0 {
236            return None;
237        }
238
239        unsafe {
240            let p = self.idx.as_ptr();
241            self.len -= 1;
242            self.idx = ptr::NonNull::new_unchecked(p.add(1));
243            Some(GString::from_glib_full(*p))
244        }
245    }
246
247    #[inline]
248    fn size_hint(&self) -> (usize, Option<usize>) {
249        (self.len, Some(self.len))
250    }
251
252    #[inline]
253    fn count(self) -> usize {
254        self.len
255    }
256
257    #[inline]
258    fn last(mut self) -> Option<GString> {
259        if self.len == 0 {
260            None
261        } else {
262            self.len -= 1;
263            Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) })
264        }
265    }
266}
267
268impl DoubleEndedIterator for IntoIter {
269    #[inline]
270    fn next_back(&mut self) -> Option<GString> {
271        if self.len == 0 {
272            None
273        } else {
274            self.len -= 1;
275            Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) })
276        }
277    }
278}
279
280impl ExactSizeIterator for IntoIter {}
281
282impl std::iter::FusedIterator for IntoIter {}
283
284impl From<StrV> for Vec<GString> {
285    #[inline]
286    fn from(value: StrV) -> Self {
287        value.into_iter().collect()
288    }
289}
290
291impl From<Vec<String>> for StrV {
292    #[inline]
293    fn from(value: Vec<String>) -> Self {
294        unsafe {
295            let len = value.len();
296            let mut s = Self::with_capacity(len);
297            for (i, item) in value.into_iter().enumerate() {
298                *s.ptr.as_ptr().add(i) = GString::from(item).into_glib_ptr();
299            }
300            s.len = len;
301            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
302            s
303        }
304    }
305}
306
307impl From<Vec<&'_ str>> for StrV {
308    #[inline]
309    fn from(value: Vec<&'_ str>) -> Self {
310        value.as_slice().into()
311    }
312}
313
314impl From<Vec<GString>> for StrV {
315    #[inline]
316    fn from(value: Vec<GString>) -> Self {
317        unsafe {
318            let len = value.len();
319            let mut s = Self::with_capacity(len);
320            for (i, v) in value.into_iter().enumerate() {
321                *s.ptr.as_ptr().add(i) = v.into_glib_ptr();
322            }
323            s.len = len;
324            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
325            s
326        }
327    }
328}
329
330impl<const N: usize> From<[GString; N]> for StrV {
331    #[inline]
332    fn from(value: [GString; N]) -> Self {
333        unsafe {
334            let len = value.len();
335            let mut s = Self::with_capacity(len);
336            for (i, v) in value.into_iter().enumerate() {
337                *s.ptr.as_ptr().add(i) = v.into_glib_ptr();
338            }
339            s.len = len;
340            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
341            s
342        }
343    }
344}
345
346impl<const N: usize> From<[String; N]> for StrV {
347    #[inline]
348    fn from(value: [String; N]) -> Self {
349        unsafe {
350            let len = value.len();
351            let mut s = Self::with_capacity(len);
352            for (i, v) in value.into_iter().enumerate() {
353                *s.ptr.as_ptr().add(i) = GString::from(v).into_glib_ptr();
354            }
355            s.len = len;
356            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
357            s
358        }
359    }
360}
361
362impl<const N: usize> From<[&'_ str; N]> for StrV {
363    #[inline]
364    fn from(value: [&'_ str; N]) -> Self {
365        unsafe {
366            let mut s = Self::with_capacity(value.len());
367            for (i, item) in value.iter().enumerate() {
368                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
369            }
370            s.len = value.len();
371            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
372            s
373        }
374    }
375}
376
377impl<const N: usize> From<[&'_ GStr; N]> for StrV {
378    #[inline]
379    fn from(value: [&'_ GStr; N]) -> Self {
380        unsafe {
381            let mut s = Self::with_capacity(value.len());
382            for (i, item) in value.iter().enumerate() {
383                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
384            }
385            s.len = value.len();
386            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
387            s
388        }
389    }
390}
391
392impl From<&'_ [&'_ str]> for StrV {
393    #[inline]
394    fn from(value: &'_ [&'_ str]) -> Self {
395        unsafe {
396            let mut s = Self::with_capacity(value.len());
397            for (i, item) in value.iter().enumerate() {
398                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
399            }
400            s.len = value.len();
401            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
402            s
403        }
404    }
405}
406
407impl From<&'_ [&'_ GStr]> for StrV {
408    #[inline]
409    fn from(value: &'_ [&'_ GStr]) -> Self {
410        unsafe {
411            let mut s = Self::with_capacity(value.len());
412            for (i, item) in value.iter().enumerate() {
413                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
414            }
415            s.len = value.len();
416            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
417            s
418        }
419    }
420}
421
422impl Clone for StrV {
423    #[inline]
424    fn clone(&self) -> Self {
425        unsafe {
426            let mut s = Self::with_capacity(self.len());
427            for (i, item) in self.iter().enumerate() {
428                *s.ptr.as_ptr().add(i) = GString::from(item.as_str()).into_glib_ptr();
429            }
430            s.len = self.len();
431            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
432            s
433        }
434    }
435}
436
437impl StrV {
438    // rustdoc-stripper-ignore-next
439    /// Borrows a C array.
440    #[inline]
441    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a [GStringPtr] {
442        let mut len = 0;
443        if !ptr.is_null() {
444            while !(*ptr.add(len)).is_null() {
445                len += 1;
446            }
447        }
448        Self::from_glib_borrow_num(ptr, len)
449    }
450
451    // rustdoc-stripper-ignore-next
452    /// Borrows a C array.
453    #[inline]
454    pub unsafe fn from_glib_borrow_num<'a>(
455        ptr: *const *const c_char,
456        len: usize,
457    ) -> &'a [GStringPtr] {
458        debug_assert!(!ptr.is_null() || len == 0);
459
460        if len == 0 {
461            &[]
462        } else {
463            std::slice::from_raw_parts(ptr as *const GStringPtr, len)
464        }
465    }
466
467    // rustdoc-stripper-ignore-next
468    /// Create a new `StrV` around a C array.
469    #[inline]
470    pub unsafe fn from_glib_none_num(
471        ptr: *const *const c_char,
472        len: usize,
473        _null_terminated: bool,
474    ) -> Self {
475        debug_assert!(!ptr.is_null() || len == 0);
476
477        if len == 0 {
478            StrV::default()
479        } else {
480            // Allocate space for len + 1 pointers, one pointer for each string and a trailing
481            // null pointer.
482            let new_ptr =
483                ffi::g_malloc(mem::size_of::<*mut c_char>() * (len + 1)) as *mut *mut c_char;
484
485            // Need to clone every item because we don't own it here
486            for i in 0..len {
487                let p = ptr.add(i) as *mut *const c_char;
488                let q = new_ptr.add(i) as *mut *const c_char;
489                *q = ffi::g_strdup(*p);
490            }
491
492            *new_ptr.add(len) = ptr::null_mut();
493
494            StrV {
495                ptr: ptr::NonNull::new_unchecked(new_ptr),
496                len,
497                capacity: len + 1,
498            }
499        }
500    }
501
502    // rustdoc-stripper-ignore-next
503    /// Create a new `StrV` around a C array.
504    #[inline]
505    pub unsafe fn from_glib_container_num(
506        ptr: *mut *const c_char,
507        len: usize,
508        null_terminated: bool,
509    ) -> Self {
510        debug_assert!(!ptr.is_null() || len == 0);
511
512        if len == 0 {
513            ffi::g_free(ptr as ffi::gpointer);
514            StrV::default()
515        } else {
516            // Need to clone every item because we don't own it here
517            for i in 0..len {
518                let p = ptr.add(i);
519                *p = ffi::g_strdup(*p);
520            }
521
522            // And now it can be handled exactly the same as `from_glib_full_num()`.
523            Self::from_glib_full_num(ptr as *mut *mut c_char, len, null_terminated)
524        }
525    }
526
527    // rustdoc-stripper-ignore-next
528    /// Create a new `StrV` around a C array.
529    #[inline]
530    pub unsafe fn from_glib_full_num(
531        ptr: *mut *mut c_char,
532        len: usize,
533        null_terminated: bool,
534    ) -> Self {
535        debug_assert!(!ptr.is_null() || len == 0);
536
537        if len == 0 {
538            ffi::g_free(ptr as ffi::gpointer);
539            StrV::default()
540        } else {
541            if null_terminated {
542                return StrV {
543                    ptr: ptr::NonNull::new_unchecked(ptr),
544                    len,
545                    capacity: len + 1,
546                };
547            }
548
549            // Need to re-allocate here for adding the NULL-terminator
550            let capacity = len + 1;
551            assert_ne!(capacity, 0);
552            let ptr = ffi::g_realloc(
553                ptr as *mut _,
554                mem::size_of::<*mut c_char>().checked_mul(capacity).unwrap(),
555            ) as *mut *mut c_char;
556            *ptr.add(len) = ptr::null_mut();
557
558            StrV {
559                ptr: ptr::NonNull::new_unchecked(ptr),
560                len,
561                capacity,
562            }
563        }
564    }
565
566    // rustdoc-stripper-ignore-next
567    /// Create a new `StrV` around a `NULL`-terminated C array.
568    #[inline]
569    pub unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
570        let mut len = 0;
571        if !ptr.is_null() {
572            while !(*ptr.add(len)).is_null() {
573                len += 1;
574            }
575        }
576
577        StrV::from_glib_none_num(ptr, len, true)
578    }
579
580    // rustdoc-stripper-ignore-next
581    /// Create a new `StrV` around a `NULL`-terminated C array.
582    #[inline]
583    pub unsafe fn from_glib_container(ptr: *mut *const c_char) -> Self {
584        let mut len = 0;
585        if !ptr.is_null() {
586            while !(*ptr.add(len)).is_null() {
587                len += 1;
588            }
589        }
590
591        StrV::from_glib_container_num(ptr, len, true)
592    }
593
594    // rustdoc-stripper-ignore-next
595    /// Create a new `StrV` around a `NULL`-terminated C array.
596    #[inline]
597    pub unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
598        let mut len = 0;
599        if !ptr.is_null() {
600            while !(*ptr.add(len)).is_null() {
601                len += 1;
602            }
603        }
604
605        StrV::from_glib_full_num(ptr, len, true)
606    }
607
608    // rustdoc-stripper-ignore-next
609    /// Creates a new empty slice.
610    #[inline]
611    pub fn new() -> Self {
612        StrV {
613            ptr: ptr::NonNull::dangling(),
614            len: 0,
615            capacity: 0,
616        }
617    }
618
619    // rustdoc-stripper-ignore-next
620    /// Creates a new empty slice with the given capacity.
621    #[inline]
622    pub fn with_capacity(capacity: usize) -> Self {
623        let mut s = Self::new();
624        s.reserve(capacity);
625        s
626    }
627
628    // rustdoc-stripper-ignore-next
629    /// Returns the underlying pointer.
630    ///
631    /// This is guaranteed to be `NULL`-terminated.
632    #[inline]
633    pub fn as_ptr(&self) -> *const *mut c_char {
634        if self.len == 0 {
635            static EMPTY: [usize; 1] = [0];
636
637            EMPTY.as_ptr() as *const _
638        } else {
639            self.ptr.as_ptr()
640        }
641    }
642
643    // rustdoc-stripper-ignore-next
644    /// Consumes the slice and returns the underlying pointer.
645    ///
646    /// This is guaranteed to be `NULL`-terminated.
647    #[inline]
648    pub fn into_raw(mut self) -> *mut *mut c_char {
649        // Make sure to allocate a valid pointer that points to a
650        // NULL-pointer.
651        if self.len == 0 {
652            self.reserve(0);
653            unsafe {
654                *self.ptr.as_ptr().add(0) = ptr::null_mut();
655            }
656        }
657
658        self.len = 0;
659        self.capacity = 0;
660        self.ptr.as_ptr()
661    }
662
663    // rustdoc-stripper-ignore-next
664    /// Gets the length of the slice.
665    #[inline]
666    pub fn len(&self) -> usize {
667        self.len
668    }
669
670    // rustdoc-stripper-ignore-next
671    /// Returns `true` if the slice is empty.
672    #[inline]
673    pub fn is_empty(&self) -> bool {
674        self.len == 0
675    }
676
677    // rustdoc-stripper-ignore-next
678    /// Returns the capacity of the slice.
679    ///
680    /// This includes the space that is reserved for the `NULL`-terminator.
681    #[inline]
682    pub fn capacity(&self) -> usize {
683        self.capacity
684    }
685
686    // rustdoc-stripper-ignore-next
687    /// Sets the length of the slice to `len`.
688    ///
689    /// # SAFETY
690    ///
691    /// There must be at least `len` valid items and a `NULL`-terminator after the last item.
692    pub unsafe fn set_len(&mut self, len: usize) {
693        self.len = len;
694    }
695
696    // rustdoc-stripper-ignore-next
697    /// Reserves at least this much additional capacity.
698    #[allow(clippy::int_plus_one)]
699    pub fn reserve(&mut self, additional: usize) {
700        // Nothing new to reserve as there's still enough space
701        if self.len + additional + 1 <= self.capacity {
702            return;
703        }
704
705        let new_capacity =
706            usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1);
707        assert_ne!(new_capacity, 0);
708        assert!(new_capacity > self.capacity);
709
710        unsafe {
711            let ptr = if self.capacity == 0 {
712                ptr::null_mut()
713            } else {
714                self.ptr.as_ptr() as *mut _
715            };
716            let new_ptr = ffi::g_realloc(
717                ptr,
718                mem::size_of::<*mut c_char>()
719                    .checked_mul(new_capacity)
720                    .unwrap(),
721            ) as *mut *mut c_char;
722            if self.capacity == 0 {
723                *new_ptr = ptr::null_mut();
724            }
725            self.ptr = ptr::NonNull::new_unchecked(new_ptr);
726            self.capacity = new_capacity;
727        }
728    }
729
730    // rustdoc-stripper-ignore-next
731    /// Borrows this slice as a `&[GStringPtr]`.
732    #[inline]
733    pub const fn as_slice(&self) -> &[GStringPtr] {
734        unsafe {
735            if self.len == 0 {
736                &[]
737            } else {
738                std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len)
739            }
740        }
741    }
742
743    // rustdoc-stripper-ignore-next
744    /// Removes all items from the slice.
745    #[inline]
746    pub fn clear(&mut self) {
747        unsafe {
748            for i in 0..self.len {
749                ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer);
750            }
751
752            self.len = 0;
753        }
754    }
755
756    // rustdoc-stripper-ignore-next
757    /// Clones and appends all elements in `slice` to the slice.
758    #[inline]
759    pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) {
760        // Nothing new to reserve as there's still enough space
761        if self.len + other.len() + 1 > self.capacity {
762            self.reserve(other.len());
763        }
764
765        unsafe {
766            for item in other {
767                *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr();
768                self.len += 1;
769            }
770
771            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
772        }
773    }
774
775    // rustdoc-stripper-ignore-next
776    /// Inserts `item` at position `index` of the slice, shifting all elements after it to the
777    /// right.
778    #[inline]
779    pub fn insert(&mut self, index: usize, item: GString) {
780        assert!(index <= self.len);
781
782        // Nothing new to reserve as there's still enough space
783        if self.len + 1 + 1 > self.capacity {
784            self.reserve(1);
785        }
786
787        unsafe {
788            if index == self.len {
789                *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
790            } else {
791                let p = self.ptr.as_ptr().add(index);
792                ptr::copy(p, p.add(1), self.len - index);
793                *self.ptr.as_ptr().add(index) = item.into_glib_ptr();
794            }
795
796            self.len += 1;
797
798            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
799        }
800    }
801
802    // rustdoc-stripper-ignore-next
803    /// Pushes `item` to the end of the slice.
804    #[inline]
805    pub fn push(&mut self, item: GString) {
806        // Nothing new to reserve as there's still enough space
807        if self.len + 1 + 1 > self.capacity {
808            self.reserve(1);
809        }
810
811        unsafe {
812            *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
813            self.len += 1;
814
815            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
816        }
817    }
818
819    // rustdoc-stripper-ignore-next
820    /// Removes item from position `index` of the slice, shifting all elements after it to the
821    /// left.
822    #[inline]
823    pub fn remove(&mut self, index: usize) -> GString {
824        assert!(index < self.len);
825
826        unsafe {
827            let p = self.ptr.as_ptr().add(index);
828            let item = *p;
829            ptr::copy(p.add(1), p, self.len - index - 1);
830
831            self.len -= 1;
832
833            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
834
835            GString::from_glib_full(item)
836        }
837    }
838
839    // rustdoc-stripper-ignore-next
840    /// Swaps item from position `index` of the slice and returns it.
841    #[inline]
842    pub fn swap(&mut self, index: usize, new_item: GString) -> GString {
843        assert!(index < self.len);
844
845        unsafe {
846            let p = self.ptr.as_ptr().add(index);
847            let item = *p;
848            *p = new_item.into_glib_ptr();
849
850            GString::from_glib_full(item)
851        }
852    }
853
854    // rustdoc-stripper-ignore-next
855    /// Removes the last item of the slice and returns it.
856    #[inline]
857    pub fn pop(&mut self) -> Option<GString> {
858        if self.len == 0 {
859            return None;
860        }
861
862        unsafe {
863            self.len -= 1;
864            let p = self.ptr.as_ptr().add(self.len);
865            let item = *p;
866
867            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
868
869            Some(GString::from_glib_full(item))
870        }
871    }
872
873    // rustdoc-stripper-ignore-next
874    /// Shortens the slice by keeping the last `len` items.
875    ///
876    /// If there are fewer than `len` items then this has no effect.
877    #[inline]
878    pub fn truncate(&mut self, len: usize) {
879        if self.len <= len {
880            return;
881        }
882
883        unsafe {
884            while self.len > len {
885                self.len -= 1;
886                let p = self.ptr.as_ptr().add(self.len);
887                ffi::g_free(*p as ffi::gpointer);
888                *p = ptr::null_mut();
889            }
890        }
891    }
892
893    // rustdoc-stripper-ignore-next
894    /// Joins the strings into a longer string, with an optional separator
895    #[inline]
896    #[doc(alias = "g_strjoinv")]
897    pub fn join(&self, separator: Option<impl IntoGStr>) -> GString {
898        separator.run_with_gstr(|separator| unsafe {
899            from_glib_full(ffi::g_strjoinv(
900                separator.to_glib_none().0,
901                self.as_ptr() as *mut _,
902            ))
903        })
904    }
905
906    // rustdoc-stripper-ignore-next
907    /// Checks whether the `StrV` contains the specified string
908    #[inline]
909    #[doc(alias = "g_strv_contains")]
910    pub fn contains(&self, s: impl IntoGStr) -> bool {
911        s.run_with_gstr(|s| unsafe {
912            from_glib(ffi::g_strv_contains(
913                self.as_ptr() as *const _,
914                s.to_glib_none().0,
915            ))
916        })
917    }
918}
919
920impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV {
921    #[inline]
922    unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self {
923        Self::from_glib_none_num(ptr as *const *const c_char, num, false)
924    }
925
926    #[inline]
927    unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self {
928        Self::from_glib_container_num(ptr as *mut *const c_char, num, false)
929    }
930
931    #[inline]
932    unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self {
933        Self::from_glib_full_num(ptr, num, false)
934    }
935}
936
937impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV {
938    unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self {
939        Self::from_glib_none_num(ptr as *const *const c_char, num, false)
940    }
941
942    unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self {
943        unimplemented!();
944    }
945
946    unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self {
947        unimplemented!();
948    }
949}
950
951impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV {
952    #[inline]
953    unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self {
954        Self::from_glib_none(ptr as *const *const c_char)
955    }
956
957    #[inline]
958    unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self {
959        Self::from_glib_container(ptr as *mut *const c_char)
960    }
961
962    #[inline]
963    unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
964        Self::from_glib_full(ptr)
965    }
966}
967
968impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV {
969    #[inline]
970    unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self {
971        Self::from_glib_none(ptr as *const *const c_char)
972    }
973
974    unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self {
975        unimplemented!();
976    }
977
978    unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self {
979        unimplemented!();
980    }
981}
982
983impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV {
984    type Storage = PhantomData<&'a Self>;
985
986    #[inline]
987    fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
988        Stash(self.as_ptr() as *mut _, PhantomData)
989    }
990
991    #[inline]
992    fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
993        unsafe {
994            let ptr =
995                ffi::g_malloc(mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char;
996            ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
997            Stash(ptr, PhantomData)
998        }
999    }
1000
1001    #[inline]
1002    fn to_glib_full(&self) -> *mut *mut c_char {
1003        self.clone().into_raw()
1004    }
1005}
1006
1007impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV {
1008    type Storage = PhantomData<&'a Self>;
1009
1010    #[inline]
1011    fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> {
1012        Stash(self.as_ptr(), PhantomData)
1013    }
1014}
1015
1016impl IntoGlibPtr<*mut *mut c_char> for StrV {
1017    #[inline]
1018    unsafe fn into_glib_ptr(self) -> *mut *mut c_char {
1019        self.into_raw()
1020    }
1021}
1022
1023impl StaticType for StrV {
1024    #[inline]
1025    fn static_type() -> crate::Type {
1026        <Vec<String>>::static_type()
1027    }
1028}
1029
1030impl StaticType for &'_ [GStringPtr] {
1031    #[inline]
1032    fn static_type() -> crate::Type {
1033        <Vec<String>>::static_type()
1034    }
1035}
1036
1037impl crate::value::ValueType for StrV {
1038    type Type = Vec<String>;
1039}
1040
1041unsafe impl<'a> crate::value::FromValue<'a> for StrV {
1042    type Checker = crate::value::GenericValueTypeChecker<Self>;
1043
1044    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1045        let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char;
1046        FromGlibPtrContainer::from_glib_full(ptr)
1047    }
1048}
1049
1050unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] {
1051    type Checker = crate::value::GenericValueTypeChecker<Self>;
1052
1053    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1054        let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1055        StrV::from_glib_borrow(ptr)
1056    }
1057}
1058
1059impl crate::value::ToValue for StrV {
1060    fn to_value(&self) -> crate::value::Value {
1061        unsafe {
1062            let mut value = crate::value::Value::for_value_type::<Self>();
1063            gobject_ffi::g_value_set_boxed(
1064                value.to_glib_none_mut().0,
1065                self.as_ptr() as ffi::gpointer,
1066            );
1067            value
1068        }
1069    }
1070
1071    fn value_type(&self) -> crate::Type {
1072        <StrV as StaticType>::static_type()
1073    }
1074}
1075
1076impl From<StrV> for crate::Value {
1077    #[inline]
1078    fn from(s: StrV) -> Self {
1079        unsafe {
1080            let mut value = crate::value::Value::for_value_type::<StrV>();
1081            gobject_ffi::g_value_take_boxed(
1082                value.to_glib_none_mut().0,
1083                s.into_raw() as ffi::gpointer,
1084            );
1085            value
1086        }
1087    }
1088}
1089
1090// rustdoc-stripper-ignore-next
1091/// A trait to accept both `&[T]` or `StrV` as an argument.
1092pub trait IntoStrV {
1093    // rustdoc-stripper-ignore-next
1094    /// Runs the given closure with a `NULL`-terminated array.
1095    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R;
1096}
1097
1098impl IntoStrV for StrV {
1099    #[inline]
1100    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1101        <&Self>::run_with_strv(&self, f)
1102    }
1103}
1104
1105impl IntoStrV for &'_ StrV {
1106    #[inline]
1107    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1108        f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) })
1109    }
1110}
1111
1112// rustdoc-stripper-ignore-next
1113/// Maximum number of pointers to stack-allocate before falling back to a heap allocation.
1114///
1115/// The beginning will be used for the pointers, the remainder for the actual string content.
1116const MAX_STACK_ALLOCATION: usize = 16;
1117
1118impl IntoStrV for Vec<GString> {
1119    #[inline]
1120    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1121        self.as_slice().run_with_strv(f)
1122    }
1123}
1124
1125impl IntoStrV for Vec<&'_ GString> {
1126    #[inline]
1127    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1128        self.as_slice().run_with_strv(f)
1129    }
1130}
1131
1132impl IntoStrV for Vec<&'_ GStr> {
1133    #[inline]
1134    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1135        self.as_slice().run_with_strv(f)
1136    }
1137}
1138
1139impl IntoStrV for Vec<&'_ str> {
1140    #[inline]
1141    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1142        self.as_slice().run_with_strv(f)
1143    }
1144}
1145
1146impl IntoStrV for Vec<String> {
1147    #[inline]
1148    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1149        self.as_slice().run_with_strv(f)
1150    }
1151}
1152
1153impl IntoStrV for Vec<&'_ String> {
1154    #[inline]
1155    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1156        self.as_slice().run_with_strv(f)
1157    }
1158}
1159
1160impl IntoStrV for &[GString] {
1161    #[inline]
1162    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1163        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1164
1165        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1166            unsafe {
1167                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1168                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1169
1170                for (i, item) in self.iter().enumerate() {
1171                    *ptrs.add(i) = item.as_ptr() as *mut _;
1172                }
1173                *ptrs.add(self.len()) = ptr::null_mut();
1174
1175                f(std::slice::from_raw_parts(ptrs, self.len()))
1176            }
1177        } else {
1178            let mut s = StrV::with_capacity(self.len());
1179            s.extend_from_slice(self);
1180            s.run_with_strv(f)
1181        }
1182    }
1183}
1184
1185impl IntoStrV for &[&GString] {
1186    #[inline]
1187    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1188        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1189
1190        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1191            unsafe {
1192                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1193                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1194
1195                for (i, item) in self.iter().enumerate() {
1196                    *ptrs.add(i) = item.as_ptr() as *mut _;
1197                }
1198                *ptrs.add(self.len()) = ptr::null_mut();
1199
1200                f(std::slice::from_raw_parts(ptrs, self.len()))
1201            }
1202        } else {
1203            let mut s = StrV::with_capacity(self.len());
1204            s.extend_from_slice(self);
1205            s.run_with_strv(f)
1206        }
1207    }
1208}
1209
1210impl IntoStrV for &[&GStr] {
1211    #[inline]
1212    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1213        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1214
1215        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1216            unsafe {
1217                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1218                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1219
1220                for (i, item) in self.iter().enumerate() {
1221                    *ptrs.add(i) = item.as_ptr() as *mut _;
1222                }
1223                *ptrs.add(self.len()) = ptr::null_mut();
1224
1225                f(std::slice::from_raw_parts(ptrs, self.len()))
1226            }
1227        } else {
1228            let mut s = StrV::with_capacity(self.len());
1229            s.extend_from_slice(self);
1230            s.run_with_strv(f)
1231        }
1232    }
1233}
1234
1235impl IntoStrV for &[&str] {
1236    #[inline]
1237    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1238        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1239            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1240
1241        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1242            unsafe {
1243                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1244                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1245                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1246
1247                for (i, item) in self.iter().enumerate() {
1248                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1249                    *strs.add(item.len()) = 0;
1250                    *ptrs.add(i) = strs;
1251                    strs = strs.add(item.len() + 1);
1252                }
1253                *ptrs.add(self.len()) = ptr::null_mut();
1254
1255                f(std::slice::from_raw_parts(ptrs, self.len()))
1256            }
1257        } else {
1258            let mut s = StrV::with_capacity(self.len());
1259            s.extend_from_slice(self);
1260            s.run_with_strv(f)
1261        }
1262    }
1263}
1264
1265impl IntoStrV for &[String] {
1266    #[inline]
1267    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1268        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1269            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1270
1271        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1272            unsafe {
1273                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1274                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1275                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1276
1277                for (i, item) in self.iter().enumerate() {
1278                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1279                    *strs.add(item.len()) = 0;
1280                    *ptrs.add(i) = strs;
1281                    strs = strs.add(item.len() + 1);
1282                }
1283                *ptrs.add(self.len()) = ptr::null_mut();
1284
1285                f(std::slice::from_raw_parts(ptrs, self.len()))
1286            }
1287        } else {
1288            let mut s = StrV::with_capacity(self.len());
1289            s.extend_from_slice(self);
1290            s.run_with_strv(f)
1291        }
1292    }
1293}
1294
1295impl IntoStrV for &[&String] {
1296    #[inline]
1297    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1298        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1299            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1300
1301        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1302            unsafe {
1303                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1304                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1305                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1306
1307                for (i, item) in self.iter().enumerate() {
1308                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1309                    *strs.add(item.len()) = 0;
1310                    *ptrs.add(i) = strs;
1311                    strs = strs.add(item.len() + 1);
1312                }
1313                *ptrs.add(self.len()) = ptr::null_mut();
1314
1315                f(std::slice::from_raw_parts(ptrs, self.len()))
1316            }
1317        } else {
1318            let mut s = StrV::with_capacity(self.len());
1319            s.extend_from_slice(self);
1320            s.run_with_strv(f)
1321        }
1322    }
1323}
1324
1325impl<const N: usize> IntoStrV for [GString; N] {
1326    #[inline]
1327    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1328        self.as_slice().run_with_strv(f)
1329    }
1330}
1331
1332impl<const N: usize> IntoStrV for [&'_ GString; N] {
1333    #[inline]
1334    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1335        self.as_slice().run_with_strv(f)
1336    }
1337}
1338
1339impl<const N: usize> IntoStrV for [&'_ GStr; N] {
1340    #[inline]
1341    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1342        self.as_slice().run_with_strv(f)
1343    }
1344}
1345
1346impl<const N: usize> IntoStrV for [&'_ str; N] {
1347    #[inline]
1348    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1349        self.as_slice().run_with_strv(f)
1350    }
1351}
1352
1353impl<const N: usize> IntoStrV for [String; N] {
1354    #[inline]
1355    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1356        self.as_slice().run_with_strv(f)
1357    }
1358}
1359
1360impl<const N: usize> IntoStrV for [&'_ String; N] {
1361    #[inline]
1362    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1363        self.as_slice().run_with_strv(f)
1364    }
1365}
1366
1367#[cfg(test)]
1368mod test {
1369    use super::*;
1370
1371    #[test]
1372    fn test_from_glib_full() {
1373        let items = ["str1", "str2", "str3", "str4"];
1374
1375        let slice = unsafe {
1376            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char;
1377            *ptr.add(0) = items[0].to_glib_full();
1378            *ptr.add(1) = items[1].to_glib_full();
1379            *ptr.add(2) = items[2].to_glib_full();
1380            *ptr.add(3) = items[3].to_glib_full();
1381
1382            StrV::from_glib_full_num(ptr, 4, false)
1383        };
1384
1385        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1386            assert_eq!(a, b);
1387        }
1388    }
1389
1390    #[test]
1391    fn test_from_glib_container() {
1392        let items = [
1393            crate::gstr!("str1"),
1394            crate::gstr!("str2"),
1395            crate::gstr!("str3"),
1396            crate::gstr!("str4"),
1397        ];
1398
1399        let slice = unsafe {
1400            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1401            *ptr.add(0) = items[0].as_ptr();
1402            *ptr.add(1) = items[1].as_ptr();
1403            *ptr.add(2) = items[2].as_ptr();
1404            *ptr.add(3) = items[3].as_ptr();
1405
1406            StrV::from_glib_container_num(ptr, 4, false)
1407        };
1408
1409        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1410            assert_eq!(a, b);
1411        }
1412    }
1413
1414    #[test]
1415    fn test_from_glib_none() {
1416        let items = [
1417            crate::gstr!("str1"),
1418            crate::gstr!("str2"),
1419            crate::gstr!("str3"),
1420            crate::gstr!("str4"),
1421        ];
1422
1423        let slice = unsafe {
1424            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1425            *ptr.add(0) = items[0].as_ptr();
1426            *ptr.add(1) = items[1].as_ptr();
1427            *ptr.add(2) = items[2].as_ptr();
1428            *ptr.add(3) = items[3].as_ptr();
1429
1430            let res = StrV::from_glib_none_num(ptr, 4, false);
1431            ffi::g_free(ptr as ffi::gpointer);
1432            res
1433        };
1434
1435        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1436            assert_eq!(a, b);
1437        }
1438    }
1439
1440    #[test]
1441    fn test_from_slice() {
1442        let items = [
1443            crate::gstr!("str1"),
1444            crate::gstr!("str2"),
1445            crate::gstr!("str3"),
1446        ];
1447
1448        let slice1 = StrV::from(&items[..]);
1449        let slice2 = StrV::from(items);
1450        assert_eq!(slice1.len(), 3);
1451        assert_eq!(slice1, slice2);
1452    }
1453
1454    #[test]
1455    fn test_safe_api() {
1456        let items = [
1457            crate::gstr!("str1"),
1458            crate::gstr!("str2"),
1459            crate::gstr!("str3"),
1460        ];
1461
1462        let mut slice = StrV::from(&items[..]);
1463        assert_eq!(slice.len(), 3);
1464        slice.push(GString::from("str4"));
1465        assert_eq!(slice.len(), 4);
1466
1467        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1468            assert_eq!(a, b);
1469        }
1470        assert_eq!(slice[3], "str4");
1471
1472        let vec = Vec::from(slice);
1473        assert_eq!(vec.len(), 4);
1474        for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
1475            assert_eq!(a, b);
1476        }
1477        assert_eq!(vec[3], "str4");
1478
1479        let mut slice = StrV::from(vec);
1480        assert_eq!(slice.len(), 4);
1481        let e = slice.pop().unwrap();
1482        assert_eq!(e, "str4");
1483        assert_eq!(slice.len(), 3);
1484        slice.insert(2, e);
1485        assert_eq!(slice.len(), 4);
1486        assert_eq!(slice[0], "str1");
1487        assert_eq!(slice[1], "str2");
1488        assert_eq!(slice[2], "str4");
1489        assert_eq!(slice[3], "str3");
1490        let e = slice.remove(2);
1491        assert_eq!(e, "str4");
1492        assert_eq!(slice.len(), 3);
1493        slice.push(e);
1494        assert_eq!(slice.len(), 4);
1495
1496        for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
1497            assert_eq!(*a, b);
1498        }
1499    }
1500
1501    #[test]
1502    fn test_into_strv() {
1503        let items = ["str1", "str2", "str3", "str4"];
1504
1505        items[..].run_with_strv(|s| unsafe {
1506            assert!((*s.as_ptr().add(4)).is_null());
1507            assert_eq!(s.len(), items.len());
1508            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1509            assert_eq!(s, items);
1510        });
1511
1512        Vec::from(&items[..]).run_with_strv(|s| unsafe {
1513            assert!((*s.as_ptr().add(4)).is_null());
1514            assert_eq!(s.len(), items.len());
1515            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1516            assert_eq!(s, items);
1517        });
1518
1519        StrV::from(&items[..]).run_with_strv(|s| unsafe {
1520            assert!((*s.as_ptr().add(4)).is_null());
1521            assert_eq!(s.len(), items.len());
1522            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1523            assert_eq!(s, items);
1524        });
1525
1526        let v = items.iter().copied().map(String::from).collect::<Vec<_>>();
1527        items.run_with_strv(|s| unsafe {
1528            assert!((*s.as_ptr().add(4)).is_null());
1529            assert_eq!(s.len(), v.len());
1530            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1531            assert_eq!(s, items);
1532        });
1533
1534        let v = items.iter().copied().map(GString::from).collect::<Vec<_>>();
1535        items.run_with_strv(|s| unsafe {
1536            assert!((*s.as_ptr().add(4)).is_null());
1537            assert_eq!(s.len(), v.len());
1538            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1539            assert_eq!(s, items);
1540        });
1541    }
1542
1543    #[test]
1544    fn test_join() {
1545        let items = [
1546            crate::gstr!("str1"),
1547            crate::gstr!("str2"),
1548            crate::gstr!("str3"),
1549        ];
1550
1551        let strv = StrV::from(&items[..]);
1552        assert_eq!(strv.join(None::<&str>), "str1str2str3");
1553        assert_eq!(strv.join(Some(",")), "str1,str2,str3");
1554    }
1555
1556    #[test]
1557    fn test_contains() {
1558        let items = [
1559            crate::gstr!("str1"),
1560            crate::gstr!("str2"),
1561            crate::gstr!("str3"),
1562        ];
1563
1564        let strv = StrV::from(&items[..]);
1565        assert!(strv.contains("str2"));
1566        assert!(!strv.contains("str4"));
1567    }
1568}