1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Bindings to libgit2's raw `git_strarray` type

use std::iter::FusedIterator;
use std::ops::Range;
use std::str;

use crate::raw;
use crate::util::Binding;

/// A string array structure used by libgit2
///
/// Some APIs return arrays of strings which originate from libgit2. This
/// wrapper type behaves a little like `Vec<&str>` but does so without copying
/// the underlying strings until necessary.
pub struct StringArray {
    raw: raw::git_strarray,
}

/// A forward iterator over the strings of an array, casted to `&str`.
pub struct Iter<'a> {
    range: Range<usize>,
    arr: &'a StringArray,
}

/// A forward iterator over the strings of an array, casted to `&[u8]`.
pub struct IterBytes<'a> {
    range: Range<usize>,
    arr: &'a StringArray,
}

impl StringArray {
    /// Returns None if the i'th string is not utf8 or if i is out of bounds.
    pub fn get(&self, i: usize) -> Option<&str> {
        self.get_bytes(i).and_then(|s| str::from_utf8(s).ok())
    }

    /// Returns None if `i` is out of bounds.
    pub fn get_bytes(&self, i: usize) -> Option<&[u8]> {
        if i < self.raw.count as usize {
            unsafe {
                let ptr = *self.raw.strings.add(i) as *const _;
                Some(crate::opt_bytes(self, ptr).unwrap())
            }
        } else {
            None
        }
    }

    /// Returns an iterator over the strings contained within this array.
    ///
    /// The iterator yields `Option<&str>` as it is unknown whether the contents
    /// are utf-8 or not.
    pub fn iter(&self) -> Iter<'_> {
        Iter {
            range: 0..self.len(),
            arr: self,
        }
    }

    /// Returns an iterator over the strings contained within this array,
    /// yielding byte slices.
    pub fn iter_bytes(&self) -> IterBytes<'_> {
        IterBytes {
            range: 0..self.len(),
            arr: self,
        }
    }

    /// Returns the number of strings in this array.
    pub fn len(&self) -> usize {
        self.raw.count as usize
    }

    /// Return `true` if this array is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl Binding for StringArray {
    type Raw = raw::git_strarray;
    unsafe fn from_raw(raw: raw::git_strarray) -> StringArray {
        StringArray { raw }
    }
    fn raw(&self) -> raw::git_strarray {
        self.raw
    }
}

impl<'a> IntoIterator for &'a StringArray {
    type Item = Option<&'a str>;
    type IntoIter = Iter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = Option<&'a str>;
    fn next(&mut self) -> Option<Option<&'a str>> {
        self.range.next().map(|i| self.arr.get(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.range.size_hint()
    }
}
impl<'a> DoubleEndedIterator for Iter<'a> {
    fn next_back(&mut self) -> Option<Option<&'a str>> {
        self.range.next_back().map(|i| self.arr.get(i))
    }
}
impl<'a> FusedIterator for Iter<'a> {}
impl<'a> ExactSizeIterator for Iter<'a> {}

impl<'a> Iterator for IterBytes<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        self.range.next().and_then(|i| self.arr.get_bytes(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.range.size_hint()
    }
}
impl<'a> DoubleEndedIterator for IterBytes<'a> {
    fn next_back(&mut self) -> Option<&'a [u8]> {
        self.range.next_back().and_then(|i| self.arr.get_bytes(i))
    }
}
impl<'a> FusedIterator for IterBytes<'a> {}
impl<'a> ExactSizeIterator for IterBytes<'a> {}

impl Drop for StringArray {
    fn drop(&mut self) {
        unsafe { raw::git_strarray_free(&mut self.raw) }
    }
}