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
use std::ffi::CString;
use std::ptr;

use crate::util::Binding;
use crate::{raw, Error, Signature};

/// A structure to represent a repository's .mailmap file.
///
/// The representation cannot be written to disk.
pub struct Mailmap {
    raw: *mut raw::git_mailmap,
}

impl Binding for Mailmap {
    type Raw = *mut raw::git_mailmap;

    unsafe fn from_raw(ptr: *mut raw::git_mailmap) -> Mailmap {
        Mailmap { raw: ptr }
    }

    fn raw(&self) -> *mut raw::git_mailmap {
        self.raw
    }
}

impl Drop for Mailmap {
    fn drop(&mut self) {
        unsafe {
            raw::git_mailmap_free(self.raw);
        }
    }
}

impl Mailmap {
    /// Creates an empty, in-memory mailmap object.
    pub fn new() -> Result<Mailmap, Error> {
        crate::init();
        let mut ret = ptr::null_mut();
        unsafe {
            try_call!(raw::git_mailmap_new(&mut ret));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Creates an in-memory mailmap object representing the given buffer.
    pub fn from_buffer(buf: &str) -> Result<Mailmap, Error> {
        crate::init();
        let mut ret = ptr::null_mut();
        let len = buf.len();
        let buf = CString::new(buf)?;
        unsafe {
            try_call!(raw::git_mailmap_from_buffer(&mut ret, buf, len));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Adds a new entry to this in-memory mailmap object.
    pub fn add_entry(
        &mut self,
        real_name: Option<&str>,
        real_email: Option<&str>,
        replace_name: Option<&str>,
        replace_email: &str,
    ) -> Result<(), Error> {
        let real_name = crate::opt_cstr(real_name)?;
        let real_email = crate::opt_cstr(real_email)?;
        let replace_name = crate::opt_cstr(replace_name)?;
        let replace_email = CString::new(replace_email)?;
        unsafe {
            try_call!(raw::git_mailmap_add_entry(
                self.raw,
                real_name,
                real_email,
                replace_name,
                replace_email
            ));
            Ok(())
        }
    }

    /// Resolves a signature to its real name and email address.
    pub fn resolve_signature(&self, sig: &Signature<'_>) -> Result<Signature<'static>, Error> {
        let mut ret = ptr::null_mut();
        unsafe {
            try_call!(raw::git_mailmap_resolve_signature(
                &mut ret,
                &*self.raw,
                sig.raw()
            ));
            Ok(Binding::from_raw(ret))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn smoke() {
        let sig_name = "name";
        let sig_email = "email";
        let sig = t!(Signature::now(sig_name, sig_email));

        let mut mm = t!(Mailmap::new());

        let mailmapped_sig = t!(mm.resolve_signature(&sig));
        assert_eq!(mailmapped_sig.name(), Some(sig_name));
        assert_eq!(mailmapped_sig.email(), Some(sig_email));

        t!(mm.add_entry(None, None, None, sig_email));
        t!(mm.add_entry(
            Some("real name"),
            Some("real@email"),
            Some(sig_name),
            sig_email,
        ));

        let mailmapped_sig = t!(mm.resolve_signature(&sig));
        assert_eq!(mailmapped_sig.name(), Some("real name"));
        assert_eq!(mailmapped_sig.email(), Some("real@email"));
    }

    #[test]
    fn from_buffer() {
        let buf = "<prøper@emæil> <email>";
        let mm = t!(Mailmap::from_buffer(&buf));

        let sig = t!(Signature::now("name", "email"));
        let mailmapped_sig = t!(mm.resolve_signature(&sig));
        assert_eq!(mailmapped_sig.name(), Some("name"));
        assert_eq!(mailmapped_sig.email(), Some("prøper@emæil"));
    }
}