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 AsRef<StrVRef> for StrV {
118    #[inline]
119    fn as_ref(&self) -> &StrVRef {
120        self.into()
121    }
122}
123
124impl std::borrow::Borrow<StrVRef> for StrV {
125    #[inline]
126    fn borrow(&self) -> &StrVRef {
127        self.into()
128    }
129}
130
131impl std::ops::Deref for StrV {
132    type Target = StrVRef;
133
134    #[inline]
135    fn deref(&self) -> &StrVRef {
136        self.into()
137    }
138}
139
140impl std::iter::Extend<GString> for StrV {
141    #[inline]
142    fn extend<I: IntoIterator<Item = GString>>(&mut self, iter: I) {
143        let iter = iter.into_iter();
144        self.reserve(iter.size_hint().0);
145
146        for item in iter {
147            self.push(item);
148        }
149    }
150}
151
152impl<'a> std::iter::Extend<&'a str> for StrV {
153    #[inline]
154    fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
155        let iter = iter.into_iter();
156        self.reserve(iter.size_hint().0);
157
158        for item in iter {
159            self.push(GString::from(item));
160        }
161    }
162}
163
164impl std::iter::FromIterator<GString> for StrV {
165    #[inline]
166    fn from_iter<I: IntoIterator<Item = GString>>(iter: I) -> Self {
167        let iter = iter.into_iter();
168        let mut s = Self::with_capacity(iter.size_hint().0);
169        for item in iter {
170            s.push(item);
171        }
172        s
173    }
174}
175
176impl<'a> std::iter::IntoIterator for &'a StrV {
177    type Item = &'a GStringPtr;
178    type IntoIter = std::slice::Iter<'a, GStringPtr>;
179
180    #[inline]
181    fn into_iter(self) -> Self::IntoIter {
182        self.as_slice().iter()
183    }
184}
185
186impl std::iter::IntoIterator for StrV {
187    type Item = GString;
188    type IntoIter = IntoIter;
189
190    #[inline]
191    fn into_iter(self) -> Self::IntoIter {
192        IntoIter::new(self)
193    }
194}
195
196pub struct IntoIter {
197    ptr: ptr::NonNull<*mut c_char>,
198    idx: ptr::NonNull<*mut c_char>,
199    len: usize,
200    empty: bool,
201}
202
203impl IntoIter {
204    #[inline]
205    fn new(slice: StrV) -> Self {
206        let slice = mem::ManuallyDrop::new(slice);
207        IntoIter {
208            ptr: slice.ptr,
209            idx: slice.ptr,
210            len: slice.len,
211            empty: slice.capacity == 0,
212        }
213    }
214
215    // rustdoc-stripper-ignore-next
216    /// Returns the remaining items as slice.
217    #[inline]
218    pub const fn as_slice(&self) -> &[GStringPtr] {
219        unsafe {
220            if self.len == 0 {
221                &[]
222            } else {
223                std::slice::from_raw_parts(self.idx.as_ptr() as *const GStringPtr, self.len)
224            }
225        }
226    }
227}
228
229impl Drop for IntoIter {
230    #[inline]
231    fn drop(&mut self) {
232        unsafe {
233            for i in 0..self.len {
234                ffi::g_free(*self.idx.as_ptr().add(i) as ffi::gpointer);
235            }
236
237            if !self.empty {
238                ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
239            }
240        }
241    }
242}
243
244impl Iterator for IntoIter {
245    type Item = GString;
246
247    #[inline]
248    fn next(&mut self) -> Option<Self::Item> {
249        if self.len == 0 {
250            return None;
251        }
252
253        unsafe {
254            let p = self.idx.as_ptr();
255            self.len -= 1;
256            self.idx = ptr::NonNull::new_unchecked(p.add(1));
257            Some(GString::from_glib_full(*p))
258        }
259    }
260
261    #[inline]
262    fn size_hint(&self) -> (usize, Option<usize>) {
263        (self.len, Some(self.len))
264    }
265
266    #[inline]
267    fn count(self) -> usize {
268        self.len
269    }
270
271    #[inline]
272    fn last(mut self) -> Option<GString> {
273        if self.len == 0 {
274            None
275        } else {
276            self.len -= 1;
277            Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) })
278        }
279    }
280}
281
282impl DoubleEndedIterator for IntoIter {
283    #[inline]
284    fn next_back(&mut self) -> Option<GString> {
285        if self.len == 0 {
286            None
287        } else {
288            self.len -= 1;
289            Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) })
290        }
291    }
292}
293
294impl ExactSizeIterator for IntoIter {}
295
296impl std::iter::FusedIterator for IntoIter {}
297
298impl From<StrV> for Vec<GString> {
299    #[inline]
300    fn from(value: StrV) -> Self {
301        value.into_iter().collect()
302    }
303}
304
305impl From<Vec<String>> for StrV {
306    #[inline]
307    fn from(value: Vec<String>) -> Self {
308        unsafe {
309            let len = value.len();
310            let mut s = Self::with_capacity(len);
311            for (i, item) in value.into_iter().enumerate() {
312                *s.ptr.as_ptr().add(i) = GString::from(item).into_glib_ptr();
313            }
314            s.len = len;
315            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
316            s
317        }
318    }
319}
320
321impl From<Vec<&'_ str>> for StrV {
322    #[inline]
323    fn from(value: Vec<&'_ str>) -> Self {
324        value.as_slice().into()
325    }
326}
327
328impl From<Vec<GString>> for StrV {
329    #[inline]
330    fn from(value: Vec<GString>) -> Self {
331        unsafe {
332            let len = value.len();
333            let mut s = Self::with_capacity(len);
334            for (i, v) in value.into_iter().enumerate() {
335                *s.ptr.as_ptr().add(i) = v.into_glib_ptr();
336            }
337            s.len = len;
338            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
339            s
340        }
341    }
342}
343
344impl<const N: usize> From<[GString; N]> for StrV {
345    #[inline]
346    fn from(value: [GString; N]) -> Self {
347        unsafe {
348            let len = value.len();
349            let mut s = Self::with_capacity(len);
350            for (i, v) in value.into_iter().enumerate() {
351                *s.ptr.as_ptr().add(i) = v.into_glib_ptr();
352            }
353            s.len = len;
354            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
355            s
356        }
357    }
358}
359
360impl<const N: usize> From<[String; N]> for StrV {
361    #[inline]
362    fn from(value: [String; N]) -> Self {
363        unsafe {
364            let len = value.len();
365            let mut s = Self::with_capacity(len);
366            for (i, v) in value.into_iter().enumerate() {
367                *s.ptr.as_ptr().add(i) = GString::from(v).into_glib_ptr();
368            }
369            s.len = len;
370            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
371            s
372        }
373    }
374}
375
376impl<const N: usize> From<[&'_ str; N]> for StrV {
377    #[inline]
378    fn from(value: [&'_ str; N]) -> Self {
379        unsafe {
380            let mut s = Self::with_capacity(value.len());
381            for (i, item) in value.iter().enumerate() {
382                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
383            }
384            s.len = value.len();
385            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
386            s
387        }
388    }
389}
390
391impl<const N: usize> From<[&'_ GStr; N]> for StrV {
392    #[inline]
393    fn from(value: [&'_ GStr; N]) -> Self {
394        unsafe {
395            let mut s = Self::with_capacity(value.len());
396            for (i, item) in value.iter().enumerate() {
397                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
398            }
399            s.len = value.len();
400            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
401            s
402        }
403    }
404}
405
406impl From<&'_ [&'_ str]> for StrV {
407    #[inline]
408    fn from(value: &'_ [&'_ str]) -> Self {
409        unsafe {
410            let mut s = Self::with_capacity(value.len());
411            for (i, item) in value.iter().enumerate() {
412                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
413            }
414            s.len = value.len();
415            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
416            s
417        }
418    }
419}
420
421impl From<&'_ [&'_ GStr]> for StrV {
422    #[inline]
423    fn from(value: &'_ [&'_ GStr]) -> Self {
424        unsafe {
425            let mut s = Self::with_capacity(value.len());
426            for (i, item) in value.iter().enumerate() {
427                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
428            }
429            s.len = value.len();
430            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
431            s
432        }
433    }
434}
435
436impl Clone for StrV {
437    #[inline]
438    fn clone(&self) -> Self {
439        unsafe {
440            let mut s = Self::with_capacity(self.len());
441            for (i, item) in self.iter().enumerate() {
442                *s.ptr.as_ptr().add(i) = GString::from(item.as_str()).into_glib_ptr();
443            }
444            s.len = self.len();
445            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
446            s
447        }
448    }
449}
450
451impl StrV {
452    // rustdoc-stripper-ignore-next
453    /// Borrows a C array.
454    #[inline]
455    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a [GStringPtr] {
456        let mut len = 0;
457        if !ptr.is_null() {
458            while !(*ptr.add(len)).is_null() {
459                len += 1;
460            }
461        }
462        Self::from_glib_borrow_num(ptr, len)
463    }
464
465    // rustdoc-stripper-ignore-next
466    /// Borrows a C array.
467    #[inline]
468    pub unsafe fn from_glib_borrow_num<'a>(
469        ptr: *const *const c_char,
470        len: usize,
471    ) -> &'a [GStringPtr] {
472        debug_assert!(!ptr.is_null() || len == 0);
473
474        if len == 0 {
475            &[]
476        } else {
477            std::slice::from_raw_parts(ptr as *const GStringPtr, len)
478        }
479    }
480
481    // rustdoc-stripper-ignore-next
482    /// Create a new `StrV` around a C array.
483    #[inline]
484    pub unsafe fn from_glib_none_num(
485        ptr: *const *const c_char,
486        len: usize,
487        _null_terminated: bool,
488    ) -> Self {
489        debug_assert!(!ptr.is_null() || len == 0);
490
491        if len == 0 {
492            StrV::default()
493        } else {
494            // Allocate space for len + 1 pointers, one pointer for each string and a trailing
495            // null pointer.
496            let new_ptr =
497                ffi::g_malloc(mem::size_of::<*mut c_char>() * (len + 1)) as *mut *mut c_char;
498
499            // Need to clone every item because we don't own it here
500            for i in 0..len {
501                let p = ptr.add(i) as *mut *const c_char;
502                let q = new_ptr.add(i) as *mut *const c_char;
503                *q = ffi::g_strdup(*p);
504            }
505
506            *new_ptr.add(len) = ptr::null_mut();
507
508            StrV {
509                ptr: ptr::NonNull::new_unchecked(new_ptr),
510                len,
511                capacity: len + 1,
512            }
513        }
514    }
515
516    // rustdoc-stripper-ignore-next
517    /// Create a new `StrV` around a C array.
518    #[inline]
519    pub unsafe fn from_glib_container_num(
520        ptr: *mut *const c_char,
521        len: usize,
522        null_terminated: bool,
523    ) -> Self {
524        debug_assert!(!ptr.is_null() || len == 0);
525
526        if len == 0 {
527            ffi::g_free(ptr as ffi::gpointer);
528            StrV::default()
529        } else {
530            // Need to clone every item because we don't own it here
531            for i in 0..len {
532                let p = ptr.add(i);
533                *p = ffi::g_strdup(*p);
534            }
535
536            // And now it can be handled exactly the same as `from_glib_full_num()`.
537            Self::from_glib_full_num(ptr as *mut *mut c_char, len, null_terminated)
538        }
539    }
540
541    // rustdoc-stripper-ignore-next
542    /// Create a new `StrV` around a C array.
543    #[inline]
544    pub unsafe fn from_glib_full_num(
545        ptr: *mut *mut c_char,
546        len: usize,
547        null_terminated: bool,
548    ) -> Self {
549        debug_assert!(!ptr.is_null() || len == 0);
550
551        if len == 0 {
552            ffi::g_free(ptr as ffi::gpointer);
553            StrV::default()
554        } else {
555            if null_terminated {
556                return StrV {
557                    ptr: ptr::NonNull::new_unchecked(ptr),
558                    len,
559                    capacity: len + 1,
560                };
561            }
562
563            // Need to re-allocate here for adding the NULL-terminator
564            let capacity = len + 1;
565            assert_ne!(capacity, 0);
566            let ptr = ffi::g_realloc(
567                ptr as *mut _,
568                mem::size_of::<*mut c_char>().checked_mul(capacity).unwrap(),
569            ) as *mut *mut c_char;
570            *ptr.add(len) = ptr::null_mut();
571
572            StrV {
573                ptr: ptr::NonNull::new_unchecked(ptr),
574                len,
575                capacity,
576            }
577        }
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_none(ptr: *const *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_none_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_container(ptr: *mut *const 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_container_num(ptr, len, true)
606    }
607
608    // rustdoc-stripper-ignore-next
609    /// Create a new `StrV` around a `NULL`-terminated C array.
610    #[inline]
611    pub unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
612        let mut len = 0;
613        if !ptr.is_null() {
614            while !(*ptr.add(len)).is_null() {
615                len += 1;
616            }
617        }
618
619        StrV::from_glib_full_num(ptr, len, true)
620    }
621
622    // rustdoc-stripper-ignore-next
623    /// Creates a new empty slice.
624    #[inline]
625    pub fn new() -> Self {
626        StrV {
627            ptr: ptr::NonNull::dangling(),
628            len: 0,
629            capacity: 0,
630        }
631    }
632
633    // rustdoc-stripper-ignore-next
634    /// Creates a new empty slice with the given capacity.
635    #[inline]
636    pub fn with_capacity(capacity: usize) -> Self {
637        let mut s = Self::new();
638        s.reserve(capacity);
639        s
640    }
641
642    // rustdoc-stripper-ignore-next
643    /// Returns the underlying pointer.
644    ///
645    /// This is guaranteed to be `NULL`-terminated.
646    #[inline]
647    pub fn as_ptr(&self) -> *const *mut c_char {
648        if self.len == 0 {
649            static EMPTY: [usize; 1] = [0];
650
651            EMPTY.as_ptr() as *const _
652        } else {
653            self.ptr.as_ptr()
654        }
655    }
656
657    // rustdoc-stripper-ignore-next
658    /// Consumes the slice and returns the underlying pointer.
659    ///
660    /// This is guaranteed to be `NULL`-terminated.
661    #[inline]
662    pub fn into_raw(mut self) -> *mut *mut c_char {
663        // Make sure to allocate a valid pointer that points to a
664        // NULL-pointer.
665        if self.len == 0 {
666            self.reserve(0);
667            unsafe {
668                *self.ptr.as_ptr().add(0) = ptr::null_mut();
669            }
670        }
671
672        self.len = 0;
673        self.capacity = 0;
674        self.ptr.as_ptr()
675    }
676
677    // rustdoc-stripper-ignore-next
678    /// Gets the length of the slice.
679    #[inline]
680    pub fn len(&self) -> usize {
681        self.len
682    }
683
684    // rustdoc-stripper-ignore-next
685    /// Returns `true` if the slice is empty.
686    #[inline]
687    pub fn is_empty(&self) -> bool {
688        self.len == 0
689    }
690
691    // rustdoc-stripper-ignore-next
692    /// Returns the capacity of the slice.
693    ///
694    /// This includes the space that is reserved for the `NULL`-terminator.
695    #[inline]
696    pub fn capacity(&self) -> usize {
697        self.capacity
698    }
699
700    // rustdoc-stripper-ignore-next
701    /// Sets the length of the slice to `len`.
702    ///
703    /// # SAFETY
704    ///
705    /// There must be at least `len` valid items and a `NULL`-terminator after the last item.
706    pub unsafe fn set_len(&mut self, len: usize) {
707        self.len = len;
708    }
709
710    // rustdoc-stripper-ignore-next
711    /// Reserves at least this much additional capacity.
712    #[allow(clippy::int_plus_one)]
713    pub fn reserve(&mut self, additional: usize) {
714        // Nothing new to reserve as there's still enough space
715        if self.len + additional + 1 <= self.capacity {
716            return;
717        }
718
719        let new_capacity =
720            usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1);
721        assert_ne!(new_capacity, 0);
722        assert!(new_capacity > self.capacity);
723
724        unsafe {
725            let ptr = if self.capacity == 0 {
726                ptr::null_mut()
727            } else {
728                self.ptr.as_ptr() as *mut _
729            };
730            let new_ptr = ffi::g_realloc(
731                ptr,
732                mem::size_of::<*mut c_char>()
733                    .checked_mul(new_capacity)
734                    .unwrap(),
735            ) as *mut *mut c_char;
736            if self.capacity == 0 {
737                *new_ptr = ptr::null_mut();
738            }
739            self.ptr = ptr::NonNull::new_unchecked(new_ptr);
740            self.capacity = new_capacity;
741        }
742    }
743
744    // rustdoc-stripper-ignore-next
745    /// Borrows this slice as a `&[GStringPtr]`.
746    #[inline]
747    pub const fn as_slice(&self) -> &[GStringPtr] {
748        unsafe {
749            if self.len == 0 {
750                &[]
751            } else {
752                std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len)
753            }
754        }
755    }
756
757    // rustdoc-stripper-ignore-next
758    /// Removes all items from the slice.
759    #[inline]
760    pub fn clear(&mut self) {
761        unsafe {
762            for i in 0..self.len {
763                ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer);
764            }
765
766            self.len = 0;
767        }
768    }
769
770    // rustdoc-stripper-ignore-next
771    /// Clones and appends all elements in `slice` to the slice.
772    #[inline]
773    pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) {
774        // Nothing new to reserve as there's still enough space
775        if self.len + other.len() + 1 > self.capacity {
776            self.reserve(other.len());
777        }
778
779        unsafe {
780            for item in other {
781                *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr();
782                self.len += 1;
783            }
784
785            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
786        }
787    }
788
789    // rustdoc-stripper-ignore-next
790    /// Inserts `item` at position `index` of the slice, shifting all elements after it to the
791    /// right.
792    #[inline]
793    pub fn insert(&mut self, index: usize, item: GString) {
794        assert!(index <= self.len);
795
796        // Nothing new to reserve as there's still enough space
797        if self.len + 1 + 1 > self.capacity {
798            self.reserve(1);
799        }
800
801        unsafe {
802            if index == self.len {
803                *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
804            } else {
805                let p = self.ptr.as_ptr().add(index);
806                ptr::copy(p, p.add(1), self.len - index);
807                *self.ptr.as_ptr().add(index) = item.into_glib_ptr();
808            }
809
810            self.len += 1;
811
812            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
813        }
814    }
815
816    // rustdoc-stripper-ignore-next
817    /// Pushes `item` to the end of the slice.
818    #[inline]
819    pub fn push(&mut self, item: GString) {
820        // Nothing new to reserve as there's still enough space
821        if self.len + 1 + 1 > self.capacity {
822            self.reserve(1);
823        }
824
825        unsafe {
826            *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
827            self.len += 1;
828
829            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
830        }
831    }
832
833    // rustdoc-stripper-ignore-next
834    /// Removes item from position `index` of the slice, shifting all elements after it to the
835    /// left.
836    #[inline]
837    pub fn remove(&mut self, index: usize) -> GString {
838        assert!(index < self.len);
839
840        unsafe {
841            let p = self.ptr.as_ptr().add(index);
842            let item = *p;
843            ptr::copy(p.add(1), p, self.len - index - 1);
844
845            self.len -= 1;
846
847            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
848
849            GString::from_glib_full(item)
850        }
851    }
852
853    // rustdoc-stripper-ignore-next
854    /// Swaps item from position `index` of the slice and returns it.
855    #[inline]
856    pub fn swap(&mut self, index: usize, new_item: GString) -> GString {
857        assert!(index < self.len);
858
859        unsafe {
860            let p = self.ptr.as_ptr().add(index);
861            let item = *p;
862            *p = new_item.into_glib_ptr();
863
864            GString::from_glib_full(item)
865        }
866    }
867
868    // rustdoc-stripper-ignore-next
869    /// Removes the last item of the slice and returns it.
870    #[inline]
871    pub fn pop(&mut self) -> Option<GString> {
872        if self.len == 0 {
873            return None;
874        }
875
876        unsafe {
877            self.len -= 1;
878            let p = self.ptr.as_ptr().add(self.len);
879            let item = *p;
880
881            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
882
883            Some(GString::from_glib_full(item))
884        }
885    }
886
887    // rustdoc-stripper-ignore-next
888    /// Shortens the slice by keeping the last `len` items.
889    ///
890    /// If there are fewer than `len` items then this has no effect.
891    #[inline]
892    pub fn truncate(&mut self, len: usize) {
893        if self.len <= len {
894            return;
895        }
896
897        unsafe {
898            while self.len > len {
899                self.len -= 1;
900                let p = self.ptr.as_ptr().add(self.len);
901                ffi::g_free(*p as ffi::gpointer);
902                *p = ptr::null_mut();
903            }
904        }
905    }
906
907    // rustdoc-stripper-ignore-next
908    /// Joins the strings into a longer string, with an optional separator
909    #[inline]
910    #[doc(alias = "g_strjoinv")]
911    pub fn join(&self, separator: Option<impl IntoGStr>) -> GString {
912        separator.run_with_gstr(|separator| unsafe {
913            from_glib_full(ffi::g_strjoinv(
914                separator.to_glib_none().0,
915                self.as_ptr() as *mut _,
916            ))
917        })
918    }
919
920    // rustdoc-stripper-ignore-next
921    /// Checks whether the `StrV` contains the specified string
922    #[inline]
923    #[doc(alias = "g_strv_contains")]
924    pub fn contains(&self, s: impl IntoGStr) -> bool {
925        s.run_with_gstr(|s| unsafe {
926            from_glib(ffi::g_strv_contains(
927                self.as_ptr() as *const _,
928                s.to_glib_none().0,
929            ))
930        })
931    }
932}
933
934impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV {
935    #[inline]
936    unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self {
937        Self::from_glib_none_num(ptr as *const *const c_char, num, false)
938    }
939
940    #[inline]
941    unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self {
942        Self::from_glib_container_num(ptr as *mut *const c_char, num, false)
943    }
944
945    #[inline]
946    unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self {
947        Self::from_glib_full_num(ptr, num, false)
948    }
949}
950
951impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV {
952    unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self {
953        Self::from_glib_none_num(ptr as *const *const c_char, num, false)
954    }
955
956    unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self {
957        unimplemented!();
958    }
959
960    unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self {
961        unimplemented!();
962    }
963}
964
965impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV {
966    #[inline]
967    unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self {
968        Self::from_glib_none(ptr as *const *const c_char)
969    }
970
971    #[inline]
972    unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self {
973        Self::from_glib_container(ptr as *mut *const c_char)
974    }
975
976    #[inline]
977    unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
978        Self::from_glib_full(ptr)
979    }
980}
981
982impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV {
983    #[inline]
984    unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self {
985        Self::from_glib_none(ptr as *const *const c_char)
986    }
987
988    unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self {
989        unimplemented!();
990    }
991
992    unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self {
993        unimplemented!();
994    }
995}
996
997impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV {
998    type Storage = PhantomData<&'a Self>;
999
1000    #[inline]
1001    fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1002        Stash(self.as_ptr() as *mut _, PhantomData)
1003    }
1004
1005    #[inline]
1006    fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1007        unsafe {
1008            let ptr =
1009                ffi::g_malloc(mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char;
1010            ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
1011            Stash(ptr, PhantomData)
1012        }
1013    }
1014
1015    #[inline]
1016    fn to_glib_full(&self) -> *mut *mut c_char {
1017        self.clone().into_raw()
1018    }
1019}
1020
1021impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV {
1022    type Storage = PhantomData<&'a Self>;
1023
1024    #[inline]
1025    fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> {
1026        Stash(self.as_ptr(), PhantomData)
1027    }
1028}
1029
1030impl IntoGlibPtr<*mut *mut c_char> for StrV {
1031    #[inline]
1032    fn into_glib_ptr(self) -> *mut *mut c_char {
1033        self.into_raw()
1034    }
1035}
1036
1037impl StaticType for StrV {
1038    #[inline]
1039    fn static_type() -> crate::Type {
1040        <Vec<String>>::static_type()
1041    }
1042}
1043
1044impl StaticType for &'_ [GStringPtr] {
1045    #[inline]
1046    fn static_type() -> crate::Type {
1047        <Vec<String>>::static_type()
1048    }
1049}
1050
1051impl crate::value::ValueType for StrV {
1052    type Type = Vec<String>;
1053}
1054
1055unsafe impl<'a> crate::value::FromValue<'a> for StrV {
1056    type Checker = crate::value::GenericValueTypeChecker<Self>;
1057
1058    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1059        let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char;
1060        FromGlibPtrContainer::from_glib_full(ptr)
1061    }
1062}
1063
1064unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] {
1065    type Checker = crate::value::GenericValueTypeChecker<Self>;
1066
1067    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1068        let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1069        StrV::from_glib_borrow(ptr)
1070    }
1071}
1072
1073impl crate::value::ToValue for StrV {
1074    fn to_value(&self) -> crate::value::Value {
1075        unsafe {
1076            let mut value = crate::value::Value::for_value_type::<Self>();
1077            gobject_ffi::g_value_set_boxed(
1078                value.to_glib_none_mut().0,
1079                self.as_ptr() as ffi::gpointer,
1080            );
1081            value
1082        }
1083    }
1084
1085    fn value_type(&self) -> crate::Type {
1086        <StrV as StaticType>::static_type()
1087    }
1088}
1089
1090impl From<StrV> for crate::Value {
1091    #[inline]
1092    fn from(s: StrV) -> Self {
1093        unsafe {
1094            let mut value = crate::value::Value::for_value_type::<StrV>();
1095            gobject_ffi::g_value_take_boxed(
1096                value.to_glib_none_mut().0,
1097                s.into_raw() as ffi::gpointer,
1098            );
1099            value
1100        }
1101    }
1102}
1103
1104// rustdoc-stripper-ignore-next
1105/// A trait to accept both `&[T]` or `StrV` as an argument.
1106pub trait IntoStrV {
1107    // rustdoc-stripper-ignore-next
1108    /// Runs the given closure with a `NULL`-terminated array.
1109    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R;
1110}
1111
1112impl IntoStrV for StrV {
1113    #[inline]
1114    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1115        <&Self>::run_with_strv(&self, f)
1116    }
1117}
1118
1119impl IntoStrV for &'_ StrV {
1120    #[inline]
1121    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1122        f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) })
1123    }
1124}
1125
1126// rustdoc-stripper-ignore-next
1127/// Maximum number of pointers to stack-allocate before falling back to a heap allocation.
1128///
1129/// The beginning will be used for the pointers, the remainder for the actual string content.
1130const MAX_STACK_ALLOCATION: usize = 16;
1131
1132impl IntoStrV for Vec<GString> {
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<&'_ GString> {
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<&'_ GStr> {
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<&'_ str> {
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 Vec<String> {
1161    #[inline]
1162    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1163        self.as_slice().run_with_strv(f)
1164    }
1165}
1166
1167impl IntoStrV for Vec<&'_ String> {
1168    #[inline]
1169    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1170        self.as_slice().run_with_strv(f)
1171    }
1172}
1173
1174impl IntoStrV for &[GString] {
1175    #[inline]
1176    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1177        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1178
1179        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1180            unsafe {
1181                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1182                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1183
1184                for (i, item) in self.iter().enumerate() {
1185                    *ptrs.add(i) = item.as_ptr() as *mut _;
1186                }
1187                *ptrs.add(self.len()) = ptr::null_mut();
1188
1189                f(std::slice::from_raw_parts(ptrs, self.len()))
1190            }
1191        } else {
1192            let mut s = StrV::with_capacity(self.len());
1193            s.extend_from_slice(self);
1194            s.run_with_strv(f)
1195        }
1196    }
1197}
1198
1199impl IntoStrV for &[&GString] {
1200    #[inline]
1201    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1202        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1203
1204        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1205            unsafe {
1206                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1207                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1208
1209                for (i, item) in self.iter().enumerate() {
1210                    *ptrs.add(i) = item.as_ptr() as *mut _;
1211                }
1212                *ptrs.add(self.len()) = ptr::null_mut();
1213
1214                f(std::slice::from_raw_parts(ptrs, self.len()))
1215            }
1216        } else {
1217            let mut s = StrV::with_capacity(self.len());
1218            s.extend_from_slice(self);
1219            s.run_with_strv(f)
1220        }
1221    }
1222}
1223
1224impl IntoStrV for &[&GStr] {
1225    #[inline]
1226    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1227        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1228
1229        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1230            unsafe {
1231                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1232                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1233
1234                for (i, item) in self.iter().enumerate() {
1235                    *ptrs.add(i) = item.as_ptr() as *mut _;
1236                }
1237                *ptrs.add(self.len()) = ptr::null_mut();
1238
1239                f(std::slice::from_raw_parts(ptrs, self.len()))
1240            }
1241        } else {
1242            let mut s = StrV::with_capacity(self.len());
1243            s.extend_from_slice(self);
1244            s.run_with_strv(f)
1245        }
1246    }
1247}
1248
1249impl IntoStrV for &[&str] {
1250    #[inline]
1251    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1252        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1253            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1254
1255        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1256            unsafe {
1257                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1258                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1259                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1260
1261                for (i, item) in self.iter().enumerate() {
1262                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1263                    *strs.add(item.len()) = 0;
1264                    *ptrs.add(i) = strs;
1265                    strs = strs.add(item.len() + 1);
1266                }
1267                *ptrs.add(self.len()) = ptr::null_mut();
1268
1269                f(std::slice::from_raw_parts(ptrs, self.len()))
1270            }
1271        } else {
1272            let mut s = StrV::with_capacity(self.len());
1273            s.extend_from_slice(self);
1274            s.run_with_strv(f)
1275        }
1276    }
1277}
1278
1279impl IntoStrV for &[String] {
1280    #[inline]
1281    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1282        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1283            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1284
1285        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1286            unsafe {
1287                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1288                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1289                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1290
1291                for (i, item) in self.iter().enumerate() {
1292                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1293                    *strs.add(item.len()) = 0;
1294                    *ptrs.add(i) = strs;
1295                    strs = strs.add(item.len() + 1);
1296                }
1297                *ptrs.add(self.len()) = ptr::null_mut();
1298
1299                f(std::slice::from_raw_parts(ptrs, self.len()))
1300            }
1301        } else {
1302            let mut s = StrV::with_capacity(self.len());
1303            s.extend_from_slice(self);
1304            s.run_with_strv(f)
1305        }
1306    }
1307}
1308
1309impl IntoStrV for &[&String] {
1310    #[inline]
1311    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1312        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1313            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1314
1315        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1316            unsafe {
1317                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1318                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1319                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1320
1321                for (i, item) in self.iter().enumerate() {
1322                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1323                    *strs.add(item.len()) = 0;
1324                    *ptrs.add(i) = strs;
1325                    strs = strs.add(item.len() + 1);
1326                }
1327                *ptrs.add(self.len()) = ptr::null_mut();
1328
1329                f(std::slice::from_raw_parts(ptrs, self.len()))
1330            }
1331        } else {
1332            let mut s = StrV::with_capacity(self.len());
1333            s.extend_from_slice(self);
1334            s.run_with_strv(f)
1335        }
1336    }
1337}
1338
1339impl<const N: usize> IntoStrV for [GString; 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 [&'_ GString; 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 [&'_ GStr; 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 [&'_ str; 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
1367impl<const N: usize> IntoStrV for [String; N] {
1368    #[inline]
1369    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1370        self.as_slice().run_with_strv(f)
1371    }
1372}
1373
1374impl<const N: usize> IntoStrV for [&'_ String; N] {
1375    #[inline]
1376    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1377        self.as_slice().run_with_strv(f)
1378    }
1379}
1380
1381// rustdoc-stripper-ignore-next
1382/// Representation of a borrowed `NULL`-terminated C array of `NULL`-terminated UTF-8 strings.
1383///
1384/// It can be constructed safely from a `&StrV` and unsafely from a pointer to a C array.
1385/// This type is very similar to `[GStringPtr]`, but with one added constraint: the underlying C array must be `NULL`-terminated.
1386#[repr(transparent)]
1387pub struct StrVRef {
1388    inner: [GStringPtr],
1389}
1390
1391impl StrVRef {
1392    // rustdoc-stripper-ignore-next
1393    /// Borrows a C array.
1394    /// # Safety
1395    ///
1396    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1397    /// pass a pointer that does not uphold this condition.
1398    #[inline]
1399    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a StrVRef {
1400        let slice = StrV::from_glib_borrow(ptr);
1401        &*(slice as *const [GStringPtr] as *const StrVRef)
1402    }
1403
1404    // rustdoc-stripper-ignore-next
1405    /// Borrows a C array.
1406    /// # Safety
1407    ///
1408    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1409    /// pass a pointer that does not uphold this condition.
1410    #[inline]
1411    pub unsafe fn from_glib_borrow_num<'a>(ptr: *const *const c_char, len: usize) -> &'a StrVRef {
1412        let slice = StrV::from_glib_borrow_num(ptr, len);
1413        &*(slice as *const [GStringPtr] as *const StrVRef)
1414    }
1415
1416    // rustdoc-stripper-ignore-next
1417    /// Returns the underlying pointer.
1418    ///
1419    /// This is guaranteed to be nul-terminated.
1420    #[inline]
1421    pub const fn as_ptr(&self) -> *const *const c_char {
1422        self.inner.as_ptr() as *const *const _
1423    }
1424}
1425
1426impl fmt::Debug for StrVRef {
1427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1428        self.inner.fmt(f)
1429    }
1430}
1431
1432unsafe impl Send for StrVRef {}
1433
1434unsafe impl Sync for StrVRef {}
1435
1436impl PartialEq for StrVRef {
1437    #[inline]
1438    fn eq(&self, other: &Self) -> bool {
1439        self.inner == other.inner
1440    }
1441}
1442
1443impl Eq for StrVRef {}
1444
1445impl PartialOrd for StrVRef {
1446    #[inline]
1447    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1448        Some(self.cmp(other))
1449    }
1450}
1451
1452impl Ord for StrVRef {
1453    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1454        self.inner.cmp(&other.inner)
1455    }
1456}
1457
1458impl std::hash::Hash for StrVRef {
1459    #[inline]
1460    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1461        self.inner.hash(state)
1462    }
1463}
1464
1465impl PartialEq<[&'_ str]> for StrVRef {
1466    fn eq(&self, other: &[&'_ str]) -> bool {
1467        for (a, b) in Iterator::zip(self.iter(), other.iter()) {
1468            if a != b {
1469                return false;
1470            }
1471        }
1472
1473        true
1474    }
1475}
1476
1477impl PartialEq<StrVRef> for [&'_ str] {
1478    #[inline]
1479    fn eq(&self, other: &StrVRef) -> bool {
1480        other.eq(self)
1481    }
1482}
1483
1484impl Default for &StrVRef {
1485    #[inline]
1486    fn default() -> Self {
1487        const SLICE: &[*const c_char] = &[ptr::null()];
1488        // SAFETY: `SLICE` is indeed a valid nul-terminated array.
1489        unsafe { StrVRef::from_glib_borrow(SLICE.as_ptr()) }
1490    }
1491}
1492
1493impl std::ops::Deref for StrVRef {
1494    type Target = [GStringPtr];
1495
1496    #[inline]
1497    fn deref(&self) -> &[GStringPtr] {
1498        &self.inner
1499    }
1500}
1501
1502impl<'a> std::iter::IntoIterator for &'a StrVRef {
1503    type Item = &'a GStringPtr;
1504    type IntoIter = std::slice::Iter<'a, GStringPtr>;
1505
1506    #[inline]
1507    fn into_iter(self) -> Self::IntoIter {
1508        self.inner.iter()
1509    }
1510}
1511
1512impl<'a> From<&'a StrV> for &'a StrVRef {
1513    fn from(value: &'a StrV) -> Self {
1514        let slice = value.as_slice();
1515        // Safety: `&StrV` is a null-terminated C array of nul-terminated UTF-8 strings,
1516        // therefore `&StrV::as_slice()` return a a null-terminated slice of nul-terminated UTF-8 strings,
1517        // thus it is safe to convert it to `&CStr`.
1518        unsafe { &*(slice as *const [GStringPtr] as *const StrVRef) }
1519    }
1520}
1521
1522impl FromGlibContainer<*mut c_char, *const *const c_char> for &StrVRef {
1523    unsafe fn from_glib_none_num(ptr: *const *const c_char, num: usize) -> Self {
1524        StrVRef::from_glib_borrow_num(ptr, num)
1525    }
1526
1527    unsafe fn from_glib_container_num(_ptr: *const *const c_char, _num: usize) -> Self {
1528        unimplemented!();
1529    }
1530
1531    unsafe fn from_glib_full_num(_ptr: *const *const c_char, _num: usize) -> Self {
1532        unimplemented!();
1533    }
1534}
1535
1536impl FromGlibPtrContainer<*mut c_char, *const *const c_char> for &StrVRef {
1537    #[inline]
1538    unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
1539        StrVRef::from_glib_borrow(ptr)
1540    }
1541
1542    unsafe fn from_glib_container(_ptr: *const *const c_char) -> Self {
1543        unimplemented!();
1544    }
1545
1546    unsafe fn from_glib_full(_ptr: *const *const c_char) -> Self {
1547        unimplemented!();
1548    }
1549}
1550
1551impl<'a> ToGlibPtr<'a, *const *const c_char> for StrVRef {
1552    type Storage = PhantomData<&'a Self>;
1553
1554    #[inline]
1555    fn to_glib_none(&'a self) -> Stash<'a, *const *const c_char, Self> {
1556        Stash(self.as_ptr(), PhantomData)
1557    }
1558}
1559
1560impl IntoGlibPtr<*const *const c_char> for &StrVRef {
1561    #[inline]
1562    fn into_glib_ptr(self) -> *const *const c_char {
1563        self.as_ptr()
1564    }
1565}
1566
1567impl StaticType for StrVRef {
1568    #[inline]
1569    fn static_type() -> crate::Type {
1570        <Vec<String>>::static_type()
1571    }
1572}
1573
1574unsafe impl<'a> crate::value::FromValue<'a> for &'a StrVRef {
1575    type Checker = crate::value::GenericValueTypeChecker<Self>;
1576
1577    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1578        let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1579        StrVRef::from_glib_borrow(ptr)
1580    }
1581}
1582
1583#[cfg(test)]
1584mod test {
1585    use super::*;
1586
1587    #[test]
1588    fn test_from_glib_full() {
1589        let items = ["str1", "str2", "str3", "str4"];
1590
1591        let slice = unsafe {
1592            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char;
1593            *ptr.add(0) = items[0].to_glib_full();
1594            *ptr.add(1) = items[1].to_glib_full();
1595            *ptr.add(2) = items[2].to_glib_full();
1596            *ptr.add(3) = items[3].to_glib_full();
1597
1598            StrV::from_glib_full_num(ptr, 4, false)
1599        };
1600
1601        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1602            assert_eq!(a, b);
1603        }
1604    }
1605
1606    #[test]
1607    fn test_from_glib_container() {
1608        let items = [
1609            crate::gstr!("str1"),
1610            crate::gstr!("str2"),
1611            crate::gstr!("str3"),
1612            crate::gstr!("str4"),
1613        ];
1614
1615        let slice = unsafe {
1616            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1617            *ptr.add(0) = items[0].as_ptr();
1618            *ptr.add(1) = items[1].as_ptr();
1619            *ptr.add(2) = items[2].as_ptr();
1620            *ptr.add(3) = items[3].as_ptr();
1621
1622            StrV::from_glib_container_num(ptr, 4, false)
1623        };
1624
1625        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1626            assert_eq!(a, b);
1627        }
1628    }
1629
1630    #[test]
1631    fn test_from_glib_none() {
1632        let items = [
1633            crate::gstr!("str1"),
1634            crate::gstr!("str2"),
1635            crate::gstr!("str3"),
1636            crate::gstr!("str4"),
1637        ];
1638
1639        let slice = unsafe {
1640            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1641            *ptr.add(0) = items[0].as_ptr();
1642            *ptr.add(1) = items[1].as_ptr();
1643            *ptr.add(2) = items[2].as_ptr();
1644            *ptr.add(3) = items[3].as_ptr();
1645
1646            let res = StrV::from_glib_none_num(ptr, 4, false);
1647            ffi::g_free(ptr as ffi::gpointer);
1648            res
1649        };
1650
1651        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1652            assert_eq!(a, b);
1653        }
1654    }
1655
1656    #[test]
1657    fn test_from_slice() {
1658        let items = [
1659            crate::gstr!("str1"),
1660            crate::gstr!("str2"),
1661            crate::gstr!("str3"),
1662        ];
1663
1664        let slice1 = StrV::from(&items[..]);
1665        let slice2 = StrV::from(items);
1666        assert_eq!(slice1.len(), 3);
1667        assert_eq!(slice1, slice2);
1668    }
1669
1670    #[test]
1671    fn test_safe_api() {
1672        let items = [
1673            crate::gstr!("str1"),
1674            crate::gstr!("str2"),
1675            crate::gstr!("str3"),
1676        ];
1677
1678        let mut slice = StrV::from(&items[..]);
1679        assert_eq!(slice.len(), 3);
1680        slice.push(GString::from("str4"));
1681        assert_eq!(slice.len(), 4);
1682
1683        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1684            assert_eq!(a, b);
1685        }
1686        assert_eq!(slice[3], "str4");
1687
1688        let vec = Vec::from(slice);
1689        assert_eq!(vec.len(), 4);
1690        for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
1691            assert_eq!(a, b);
1692        }
1693        assert_eq!(vec[3], "str4");
1694
1695        let mut slice = StrV::from(vec);
1696        assert_eq!(slice.len(), 4);
1697        let e = slice.pop().unwrap();
1698        assert_eq!(e, "str4");
1699        assert_eq!(slice.len(), 3);
1700        slice.insert(2, e);
1701        assert_eq!(slice.len(), 4);
1702        assert_eq!(slice[0], "str1");
1703        assert_eq!(slice[1], "str2");
1704        assert_eq!(slice[2], "str4");
1705        assert_eq!(slice[3], "str3");
1706        let e = slice.remove(2);
1707        assert_eq!(e, "str4");
1708        assert_eq!(slice.len(), 3);
1709        slice.push(e);
1710        assert_eq!(slice.len(), 4);
1711
1712        for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
1713            assert_eq!(*a, b);
1714        }
1715    }
1716
1717    #[test]
1718    fn test_into_strv() {
1719        let items = ["str1", "str2", "str3", "str4"];
1720
1721        items[..].run_with_strv(|s| unsafe {
1722            assert!((*s.as_ptr().add(4)).is_null());
1723            assert_eq!(s.len(), items.len());
1724            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1725            assert_eq!(s, items);
1726        });
1727
1728        Vec::from(&items[..]).run_with_strv(|s| unsafe {
1729            assert!((*s.as_ptr().add(4)).is_null());
1730            assert_eq!(s.len(), items.len());
1731            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1732            assert_eq!(s, items);
1733        });
1734
1735        StrV::from(&items[..]).run_with_strv(|s| unsafe {
1736            assert!((*s.as_ptr().add(4)).is_null());
1737            assert_eq!(s.len(), items.len());
1738            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1739            assert_eq!(s, items);
1740        });
1741
1742        let v = items.iter().copied().map(String::from).collect::<Vec<_>>();
1743        items.run_with_strv(|s| unsafe {
1744            assert!((*s.as_ptr().add(4)).is_null());
1745            assert_eq!(s.len(), v.len());
1746            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1747            assert_eq!(s, items);
1748        });
1749
1750        let v = items.iter().copied().map(GString::from).collect::<Vec<_>>();
1751        items.run_with_strv(|s| unsafe {
1752            assert!((*s.as_ptr().add(4)).is_null());
1753            assert_eq!(s.len(), v.len());
1754            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1755            assert_eq!(s, items);
1756        });
1757    }
1758
1759    #[test]
1760    fn test_join() {
1761        let items = [
1762            crate::gstr!("str1"),
1763            crate::gstr!("str2"),
1764            crate::gstr!("str3"),
1765        ];
1766
1767        let strv = StrV::from(&items[..]);
1768        assert_eq!(strv.join(None::<&str>), "str1str2str3");
1769        assert_eq!(strv.join(Some(",")), "str1,str2,str3");
1770    }
1771
1772    #[test]
1773    fn test_contains() {
1774        let items = [
1775            crate::gstr!("str1"),
1776            crate::gstr!("str2"),
1777            crate::gstr!("str3"),
1778        ];
1779
1780        let strv = StrV::from(&items[..]);
1781        assert!(strv.contains("str2"));
1782        assert!(!strv.contains("str4"));
1783    }
1784}