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