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
//! Bindings to libgit2's raw `git_strarray` type

use std::str;
use std::ops::Range;

use raw;
use 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.offset(i as isize) as *const _;
                Some(::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: 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> 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> ExactSizeIterator for IterBytes<'a> {}

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