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        if self.len == 0 {
650            ptr::null_mut()
651        } else {
652            self.len = 0;
653            self.capacity = 0;
654            self.ptr.as_ptr()
655        }
656    }
657
658    // rustdoc-stripper-ignore-next
659    /// Gets the length of the slice.
660    #[inline]
661    pub fn len(&self) -> usize {
662        self.len
663    }
664
665    // rustdoc-stripper-ignore-next
666    /// Returns `true` if the slice is empty.
667    #[inline]
668    pub fn is_empty(&self) -> bool {
669        self.len == 0
670    }
671
672    // rustdoc-stripper-ignore-next
673    /// Returns the capacity of the slice.
674    ///
675    /// This includes the space that is reserved for the `NULL`-terminator.
676    #[inline]
677    pub fn capacity(&self) -> usize {
678        self.capacity
679    }
680
681    // rustdoc-stripper-ignore-next
682    /// Sets the length of the slice to `len`.
683    ///
684    /// # SAFETY
685    ///
686    /// There must be at least `len` valid items and a `NULL`-terminator after the last item.
687    pub unsafe fn set_len(&mut self, len: usize) {
688        self.len = len;
689    }
690
691    // rustdoc-stripper-ignore-next
692    /// Reserves at least this much additional capacity.
693    #[allow(clippy::int_plus_one)]
694    pub fn reserve(&mut self, additional: usize) {
695        // Nothing new to reserve as there's still enough space
696        if self.len + additional + 1 <= self.capacity {
697            return;
698        }
699
700        let new_capacity =
701            usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1);
702        assert_ne!(new_capacity, 0);
703        assert!(new_capacity > self.capacity);
704
705        unsafe {
706            let ptr = if self.capacity == 0 {
707                ptr::null_mut()
708            } else {
709                self.ptr.as_ptr() as *mut _
710            };
711            let new_ptr = ffi::g_realloc(
712                ptr,
713                mem::size_of::<*mut c_char>()
714                    .checked_mul(new_capacity)
715                    .unwrap(),
716            ) as *mut *mut c_char;
717            if self.capacity == 0 {
718                *new_ptr = ptr::null_mut();
719            }
720            self.ptr = ptr::NonNull::new_unchecked(new_ptr);
721            self.capacity = new_capacity;
722        }
723    }
724
725    // rustdoc-stripper-ignore-next
726    /// Borrows this slice as a `&[GStringPtr]`.
727    #[inline]
728    pub const fn as_slice(&self) -> &[GStringPtr] {
729        unsafe {
730            if self.len == 0 {
731                &[]
732            } else {
733                std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len)
734            }
735        }
736    }
737
738    // rustdoc-stripper-ignore-next
739    /// Removes all items from the slice.
740    #[inline]
741    pub fn clear(&mut self) {
742        unsafe {
743            for i in 0..self.len {
744                ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer);
745            }
746
747            self.len = 0;
748        }
749    }
750
751    // rustdoc-stripper-ignore-next
752    /// Clones and appends all elements in `slice` to the slice.
753    #[inline]
754    pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) {
755        // Nothing new to reserve as there's still enough space
756        if self.len + other.len() + 1 > self.capacity {
757            self.reserve(other.len());
758        }
759
760        unsafe {
761            for item in other {
762                *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr();
763                self.len += 1;
764            }
765
766            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
767        }
768    }
769
770    // rustdoc-stripper-ignore-next
771    /// Inserts `item` at position `index` of the slice, shifting all elements after it to the
772    /// right.
773    #[inline]
774    pub fn insert(&mut self, index: usize, item: GString) {
775        assert!(index <= self.len);
776
777        // Nothing new to reserve as there's still enough space
778        if self.len + 1 + 1 > self.capacity {
779            self.reserve(1);
780        }
781
782        unsafe {
783            if index == self.len {
784                *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
785            } else {
786                let p = self.ptr.as_ptr().add(index);
787                ptr::copy(p, p.add(1), self.len - index);
788                *self.ptr.as_ptr().add(index) = item.into_glib_ptr();
789            }
790
791            self.len += 1;
792
793            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
794        }
795    }
796
797    // rustdoc-stripper-ignore-next
798    /// Pushes `item` to the end of the slice.
799    #[inline]
800    pub fn push(&mut self, item: GString) {
801        // Nothing new to reserve as there's still enough space
802        if self.len + 1 + 1 > self.capacity {
803            self.reserve(1);
804        }
805
806        unsafe {
807            *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
808            self.len += 1;
809
810            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
811        }
812    }
813
814    // rustdoc-stripper-ignore-next
815    /// Removes item from position `index` of the slice, shifting all elements after it to the
816    /// left.
817    #[inline]
818    pub fn remove(&mut self, index: usize) -> GString {
819        assert!(index < self.len);
820
821        unsafe {
822            let p = self.ptr.as_ptr().add(index);
823            let item = *p;
824            ptr::copy(p.add(1), p, self.len - index - 1);
825
826            self.len -= 1;
827
828            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
829
830            GString::from_glib_full(item)
831        }
832    }
833
834    // rustdoc-stripper-ignore-next
835    /// Swaps item from position `index` of the slice and returns it.
836    #[inline]
837    pub fn swap(&mut self, index: usize, new_item: GString) -> GString {
838        assert!(index < self.len);
839
840        unsafe {
841            let p = self.ptr.as_ptr().add(index);
842            let item = *p;
843            *p = new_item.into_glib_ptr();
844
845            GString::from_glib_full(item)
846        }
847    }
848
849    // rustdoc-stripper-ignore-next
850    /// Removes the last item of the slice and returns it.
851    #[inline]
852    pub fn pop(&mut self) -> Option<GString> {
853        if self.len == 0 {
854            return None;
855        }
856
857        unsafe {
858            self.len -= 1;
859            let p = self.ptr.as_ptr().add(self.len);
860            let item = *p;
861
862            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
863
864            Some(GString::from_glib_full(item))
865        }
866    }
867
868    // rustdoc-stripper-ignore-next
869    /// Shortens the slice by keeping the last `len` items.
870    ///
871    /// If there are fewer than `len` items then this has no effect.
872    #[inline]
873    pub fn truncate(&mut self, len: usize) {
874        if self.len <= len {
875            return;
876        }
877
878        unsafe {
879            while self.len > len {
880                self.len -= 1;
881                let p = self.ptr.as_ptr().add(self.len);
882                ffi::g_free(*p as ffi::gpointer);
883                *p = ptr::null_mut();
884            }
885        }
886    }
887
888    // rustdoc-stripper-ignore-next
889    /// Joins the strings into a longer string, with an optional separator
890    #[inline]
891    #[doc(alias = "g_strjoinv")]
892    pub fn join(&self, separator: Option<impl IntoGStr>) -> GString {
893        separator.run_with_gstr(|separator| unsafe {
894            from_glib_full(ffi::g_strjoinv(
895                separator.to_glib_none().0,
896                self.as_ptr() as *mut _,
897            ))
898        })
899    }
900
901    // rustdoc-stripper-ignore-next
902    /// Checks whether the `StrV` contains the specified string
903    #[inline]
904    #[doc(alias = "g_strv_contains")]
905    pub fn contains(&self, s: impl IntoGStr) -> bool {
906        s.run_with_gstr(|s| unsafe {
907            from_glib(ffi::g_strv_contains(
908                self.as_ptr() as *const _,
909                s.to_glib_none().0,
910            ))
911        })
912    }
913}
914
915impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV {
916    #[inline]
917    unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self {
918        Self::from_glib_none_num(ptr as *const *const c_char, num, false)
919    }
920
921    #[inline]
922    unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self {
923        Self::from_glib_container_num(ptr as *mut *const c_char, num, false)
924    }
925
926    #[inline]
927    unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self {
928        Self::from_glib_full_num(ptr, num, false)
929    }
930}
931
932impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV {
933    unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self {
934        Self::from_glib_none_num(ptr as *const *const c_char, num, false)
935    }
936
937    unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self {
938        unimplemented!();
939    }
940
941    unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self {
942        unimplemented!();
943    }
944}
945
946impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV {
947    #[inline]
948    unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self {
949        Self::from_glib_none(ptr as *const *const c_char)
950    }
951
952    #[inline]
953    unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self {
954        Self::from_glib_container(ptr as *mut *const c_char)
955    }
956
957    #[inline]
958    unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
959        Self::from_glib_full(ptr)
960    }
961}
962
963impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV {
964    #[inline]
965    unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self {
966        Self::from_glib_none(ptr as *const *const c_char)
967    }
968
969    unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self {
970        unimplemented!();
971    }
972
973    unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self {
974        unimplemented!();
975    }
976}
977
978impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV {
979    type Storage = PhantomData<&'a Self>;
980
981    #[inline]
982    fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
983        Stash(self.as_ptr() as *mut _, PhantomData)
984    }
985
986    #[inline]
987    fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
988        unsafe {
989            let ptr =
990                ffi::g_malloc(mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char;
991            ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
992            Stash(ptr, PhantomData)
993        }
994    }
995
996    #[inline]
997    fn to_glib_full(&self) -> *mut *mut c_char {
998        self.clone().into_raw()
999    }
1000}
1001
1002impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV {
1003    type Storage = PhantomData<&'a Self>;
1004
1005    #[inline]
1006    fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> {
1007        Stash(self.as_ptr(), PhantomData)
1008    }
1009}
1010
1011impl IntoGlibPtr<*mut *mut c_char> for StrV {
1012    #[inline]
1013    unsafe fn into_glib_ptr(self) -> *mut *mut c_char {
1014        self.into_raw()
1015    }
1016}
1017
1018impl StaticType for StrV {
1019    #[inline]
1020    fn static_type() -> crate::Type {
1021        <Vec<String>>::static_type()
1022    }
1023}
1024
1025impl StaticType for &'_ [GStringPtr] {
1026    #[inline]
1027    fn static_type() -> crate::Type {
1028        <Vec<String>>::static_type()
1029    }
1030}
1031
1032impl crate::value::ValueType for StrV {
1033    type Type = Vec<String>;
1034}
1035
1036unsafe impl<'a> crate::value::FromValue<'a> for StrV {
1037    type Checker = crate::value::GenericValueTypeChecker<Self>;
1038
1039    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1040        let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char;
1041        FromGlibPtrContainer::from_glib_full(ptr)
1042    }
1043}
1044
1045unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] {
1046    type Checker = crate::value::GenericValueTypeChecker<Self>;
1047
1048    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1049        let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1050        StrV::from_glib_borrow(ptr)
1051    }
1052}
1053
1054impl crate::value::ToValue for StrV {
1055    fn to_value(&self) -> crate::value::Value {
1056        unsafe {
1057            let mut value = crate::value::Value::for_value_type::<Self>();
1058            gobject_ffi::g_value_set_boxed(
1059                value.to_glib_none_mut().0,
1060                self.as_ptr() as ffi::gpointer,
1061            );
1062            value
1063        }
1064    }
1065
1066    fn value_type(&self) -> crate::Type {
1067        <StrV as StaticType>::static_type()
1068    }
1069}
1070
1071impl From<StrV> for crate::Value {
1072    #[inline]
1073    fn from(s: StrV) -> Self {
1074        unsafe {
1075            let mut value = crate::value::Value::for_value_type::<StrV>();
1076            gobject_ffi::g_value_take_boxed(
1077                value.to_glib_none_mut().0,
1078                s.into_raw() as ffi::gpointer,
1079            );
1080            value
1081        }
1082    }
1083}
1084
1085// rustdoc-stripper-ignore-next
1086/// A trait to accept both `&[T]` or `StrV` as an argument.
1087pub trait IntoStrV {
1088    // rustdoc-stripper-ignore-next
1089    /// Runs the given closure with a `NULL`-terminated array.
1090    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R;
1091}
1092
1093impl IntoStrV for StrV {
1094    #[inline]
1095    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1096        <&Self>::run_with_strv(&self, f)
1097    }
1098}
1099
1100impl IntoStrV for &'_ StrV {
1101    #[inline]
1102    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1103        f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) })
1104    }
1105}
1106
1107// rustdoc-stripper-ignore-next
1108/// Maximum number of pointers to stack-allocate before falling back to a heap allocation.
1109///
1110/// The beginning will be used for the pointers, the remainder for the actual string content.
1111const MAX_STACK_ALLOCATION: usize = 16;
1112
1113impl IntoStrV for Vec<GString> {
1114    #[inline]
1115    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1116        self.as_slice().run_with_strv(f)
1117    }
1118}
1119
1120impl IntoStrV for Vec<&'_ GString> {
1121    #[inline]
1122    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1123        self.as_slice().run_with_strv(f)
1124    }
1125}
1126
1127impl IntoStrV for Vec<&'_ GStr> {
1128    #[inline]
1129    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1130        self.as_slice().run_with_strv(f)
1131    }
1132}
1133
1134impl IntoStrV for Vec<&'_ str> {
1135    #[inline]
1136    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1137        self.as_slice().run_with_strv(f)
1138    }
1139}
1140
1141impl IntoStrV for Vec<String> {
1142    #[inline]
1143    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1144        self.as_slice().run_with_strv(f)
1145    }
1146}
1147
1148impl IntoStrV for Vec<&'_ String> {
1149    #[inline]
1150    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1151        self.as_slice().run_with_strv(f)
1152    }
1153}
1154
1155impl IntoStrV for &[GString] {
1156    #[inline]
1157    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1158        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1159
1160        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1161            unsafe {
1162                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1163                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1164
1165                for (i, item) in self.iter().enumerate() {
1166                    *ptrs.add(i) = item.as_ptr() as *mut _;
1167                }
1168                *ptrs.add(self.len()) = ptr::null_mut();
1169
1170                f(std::slice::from_raw_parts(ptrs, self.len()))
1171            }
1172        } else {
1173            let mut s = StrV::with_capacity(self.len());
1174            s.extend_from_slice(self);
1175            s.run_with_strv(f)
1176        }
1177    }
1178}
1179
1180impl IntoStrV for &[&GString] {
1181    #[inline]
1182    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1183        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1184
1185        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1186            unsafe {
1187                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1188                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1189
1190                for (i, item) in self.iter().enumerate() {
1191                    *ptrs.add(i) = item.as_ptr() as *mut _;
1192                }
1193                *ptrs.add(self.len()) = ptr::null_mut();
1194
1195                f(std::slice::from_raw_parts(ptrs, self.len()))
1196            }
1197        } else {
1198            let mut s = StrV::with_capacity(self.len());
1199            s.extend_from_slice(self);
1200            s.run_with_strv(f)
1201        }
1202    }
1203}
1204
1205impl IntoStrV for &[&GStr] {
1206    #[inline]
1207    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1208        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1209
1210        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1211            unsafe {
1212                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1213                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1214
1215                for (i, item) in self.iter().enumerate() {
1216                    *ptrs.add(i) = item.as_ptr() as *mut _;
1217                }
1218                *ptrs.add(self.len()) = ptr::null_mut();
1219
1220                f(std::slice::from_raw_parts(ptrs, self.len()))
1221            }
1222        } else {
1223            let mut s = StrV::with_capacity(self.len());
1224            s.extend_from_slice(self);
1225            s.run_with_strv(f)
1226        }
1227    }
1228}
1229
1230impl IntoStrV for &[&str] {
1231    #[inline]
1232    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1233        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1234            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1235
1236        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1237            unsafe {
1238                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1239                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1240                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1241
1242                for (i, item) in self.iter().enumerate() {
1243                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1244                    *strs.add(item.len()) = 0;
1245                    *ptrs.add(i) = strs;
1246                    strs = strs.add(item.len() + 1);
1247                }
1248                *ptrs.add(self.len()) = ptr::null_mut();
1249
1250                f(std::slice::from_raw_parts(ptrs, self.len()))
1251            }
1252        } else {
1253            let mut s = StrV::with_capacity(self.len());
1254            s.extend_from_slice(self);
1255            s.run_with_strv(f)
1256        }
1257    }
1258}
1259
1260impl IntoStrV for &[String] {
1261    #[inline]
1262    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1263        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1264            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1265
1266        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1267            unsafe {
1268                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1269                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1270                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1271
1272                for (i, item) in self.iter().enumerate() {
1273                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1274                    *strs.add(item.len()) = 0;
1275                    *ptrs.add(i) = strs;
1276                    strs = strs.add(item.len() + 1);
1277                }
1278                *ptrs.add(self.len()) = ptr::null_mut();
1279
1280                f(std::slice::from_raw_parts(ptrs, self.len()))
1281            }
1282        } else {
1283            let mut s = StrV::with_capacity(self.len());
1284            s.extend_from_slice(self);
1285            s.run_with_strv(f)
1286        }
1287    }
1288}
1289
1290impl IntoStrV for &[&String] {
1291    #[inline]
1292    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1293        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1294            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1295
1296        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1297            unsafe {
1298                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1299                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1300                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1301
1302                for (i, item) in self.iter().enumerate() {
1303                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1304                    *strs.add(item.len()) = 0;
1305                    *ptrs.add(i) = strs;
1306                    strs = strs.add(item.len() + 1);
1307                }
1308                *ptrs.add(self.len()) = ptr::null_mut();
1309
1310                f(std::slice::from_raw_parts(ptrs, self.len()))
1311            }
1312        } else {
1313            let mut s = StrV::with_capacity(self.len());
1314            s.extend_from_slice(self);
1315            s.run_with_strv(f)
1316        }
1317    }
1318}
1319
1320impl<const N: usize> IntoStrV for [GString; N] {
1321    #[inline]
1322    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1323        self.as_slice().run_with_strv(f)
1324    }
1325}
1326
1327impl<const N: usize> IntoStrV for [&'_ GString; N] {
1328    #[inline]
1329    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1330        self.as_slice().run_with_strv(f)
1331    }
1332}
1333
1334impl<const N: usize> IntoStrV for [&'_ GStr; N] {
1335    #[inline]
1336    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1337        self.as_slice().run_with_strv(f)
1338    }
1339}
1340
1341impl<const N: usize> IntoStrV for [&'_ str; N] {
1342    #[inline]
1343    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1344        self.as_slice().run_with_strv(f)
1345    }
1346}
1347
1348impl<const N: usize> IntoStrV for [String; N] {
1349    #[inline]
1350    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1351        self.as_slice().run_with_strv(f)
1352    }
1353}
1354
1355impl<const N: usize> IntoStrV for [&'_ String; N] {
1356    #[inline]
1357    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1358        self.as_slice().run_with_strv(f)
1359    }
1360}
1361
1362#[cfg(test)]
1363mod test {
1364    use super::*;
1365
1366    #[test]
1367    fn test_from_glib_full() {
1368        let items = ["str1", "str2", "str3", "str4"];
1369
1370        let slice = unsafe {
1371            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char;
1372            *ptr.add(0) = items[0].to_glib_full();
1373            *ptr.add(1) = items[1].to_glib_full();
1374            *ptr.add(2) = items[2].to_glib_full();
1375            *ptr.add(3) = items[3].to_glib_full();
1376
1377            StrV::from_glib_full_num(ptr, 4, false)
1378        };
1379
1380        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1381            assert_eq!(a, b);
1382        }
1383    }
1384
1385    #[test]
1386    fn test_from_glib_container() {
1387        let items = [
1388            crate::gstr!("str1"),
1389            crate::gstr!("str2"),
1390            crate::gstr!("str3"),
1391            crate::gstr!("str4"),
1392        ];
1393
1394        let slice = unsafe {
1395            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1396            *ptr.add(0) = items[0].as_ptr();
1397            *ptr.add(1) = items[1].as_ptr();
1398            *ptr.add(2) = items[2].as_ptr();
1399            *ptr.add(3) = items[3].as_ptr();
1400
1401            StrV::from_glib_container_num(ptr, 4, false)
1402        };
1403
1404        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1405            assert_eq!(a, b);
1406        }
1407    }
1408
1409    #[test]
1410    fn test_from_glib_none() {
1411        let items = [
1412            crate::gstr!("str1"),
1413            crate::gstr!("str2"),
1414            crate::gstr!("str3"),
1415            crate::gstr!("str4"),
1416        ];
1417
1418        let slice = unsafe {
1419            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1420            *ptr.add(0) = items[0].as_ptr();
1421            *ptr.add(1) = items[1].as_ptr();
1422            *ptr.add(2) = items[2].as_ptr();
1423            *ptr.add(3) = items[3].as_ptr();
1424
1425            let res = StrV::from_glib_none_num(ptr, 4, false);
1426            ffi::g_free(ptr as ffi::gpointer);
1427            res
1428        };
1429
1430        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1431            assert_eq!(a, b);
1432        }
1433    }
1434
1435    #[test]
1436    fn test_from_slice() {
1437        let items = [
1438            crate::gstr!("str1"),
1439            crate::gstr!("str2"),
1440            crate::gstr!("str3"),
1441        ];
1442
1443        let slice1 = StrV::from(&items[..]);
1444        let slice2 = StrV::from(items);
1445        assert_eq!(slice1.len(), 3);
1446        assert_eq!(slice1, slice2);
1447    }
1448
1449    #[test]
1450    fn test_safe_api() {
1451        let items = [
1452            crate::gstr!("str1"),
1453            crate::gstr!("str2"),
1454            crate::gstr!("str3"),
1455        ];
1456
1457        let mut slice = StrV::from(&items[..]);
1458        assert_eq!(slice.len(), 3);
1459        slice.push(GString::from("str4"));
1460        assert_eq!(slice.len(), 4);
1461
1462        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1463            assert_eq!(a, b);
1464        }
1465        assert_eq!(slice[3], "str4");
1466
1467        let vec = Vec::from(slice);
1468        assert_eq!(vec.len(), 4);
1469        for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
1470            assert_eq!(a, b);
1471        }
1472        assert_eq!(vec[3], "str4");
1473
1474        let mut slice = StrV::from(vec);
1475        assert_eq!(slice.len(), 4);
1476        let e = slice.pop().unwrap();
1477        assert_eq!(e, "str4");
1478        assert_eq!(slice.len(), 3);
1479        slice.insert(2, e);
1480        assert_eq!(slice.len(), 4);
1481        assert_eq!(slice[0], "str1");
1482        assert_eq!(slice[1], "str2");
1483        assert_eq!(slice[2], "str4");
1484        assert_eq!(slice[3], "str3");
1485        let e = slice.remove(2);
1486        assert_eq!(e, "str4");
1487        assert_eq!(slice.len(), 3);
1488        slice.push(e);
1489        assert_eq!(slice.len(), 4);
1490
1491        for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
1492            assert_eq!(*a, b);
1493        }
1494    }
1495
1496    #[test]
1497    fn test_into_strv() {
1498        let items = ["str1", "str2", "str3", "str4"];
1499
1500        items[..].run_with_strv(|s| unsafe {
1501            assert!((*s.as_ptr().add(4)).is_null());
1502            assert_eq!(s.len(), items.len());
1503            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1504            assert_eq!(s, items);
1505        });
1506
1507        Vec::from(&items[..]).run_with_strv(|s| unsafe {
1508            assert!((*s.as_ptr().add(4)).is_null());
1509            assert_eq!(s.len(), items.len());
1510            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1511            assert_eq!(s, items);
1512        });
1513
1514        StrV::from(&items[..]).run_with_strv(|s| unsafe {
1515            assert!((*s.as_ptr().add(4)).is_null());
1516            assert_eq!(s.len(), items.len());
1517            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1518            assert_eq!(s, items);
1519        });
1520
1521        let v = items.iter().copied().map(String::from).collect::<Vec<_>>();
1522        items.run_with_strv(|s| unsafe {
1523            assert!((*s.as_ptr().add(4)).is_null());
1524            assert_eq!(s.len(), v.len());
1525            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1526            assert_eq!(s, items);
1527        });
1528
1529        let v = items.iter().copied().map(GString::from).collect::<Vec<_>>();
1530        items.run_with_strv(|s| unsafe {
1531            assert!((*s.as_ptr().add(4)).is_null());
1532            assert_eq!(s.len(), v.len());
1533            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1534            assert_eq!(s, items);
1535        });
1536    }
1537
1538    #[test]
1539    fn test_join() {
1540        let items = [
1541            crate::gstr!("str1"),
1542            crate::gstr!("str2"),
1543            crate::gstr!("str3"),
1544        ];
1545
1546        let strv = StrV::from(&items[..]);
1547        assert_eq!(strv.join(None::<&str>), "str1str2str3");
1548        assert_eq!(strv.join(Some(",")), "str1,str2,str3");
1549    }
1550
1551    #[test]
1552    fn test_contains() {
1553        let items = [
1554            crate::gstr!("str1"),
1555            crate::gstr!("str2"),
1556            crate::gstr!("str3"),
1557        ];
1558
1559        let strv = StrV::from(&items[..]);
1560        assert!(strv.contains("str2"));
1561        assert!(!strv.contains("str4"));
1562    }
1563}