1 use libc;
2 use std::ffi::CString;
3 use std::fmt;
4 use std::marker;
5 use std::mem;
6 use std::ptr;
7 use std::str;
8 
9 use crate::util::Binding;
10 use crate::{raw, Error, Time};
11 
12 /// A Signature is used to indicate authorship of various actions throughout the
13 /// library.
14 ///
15 /// Signatures contain a name, email, and timestamp. All fields can be specified
16 /// with `new` while the `now` constructor omits the timestamp. The
17 /// [`Repository::signature`] method can be used to create a default signature
18 /// with name and email values read from the configuration.
19 ///
20 /// [`Repository::signature`]: struct.Repository.html#method.signature
21 pub struct Signature<'a> {
22     raw: *mut raw::git_signature,
23     _marker: marker::PhantomData<&'a str>,
24     owned: bool,
25 }
26 
27 impl<'a> Signature<'a> {
28     /// Create a new action signature with a timestamp of 'now'.
29     ///
30     /// See `new` for more information
now(name: &str, email: &str) -> Result<Signature<'static>, Error>31     pub fn now(name: &str, email: &str) -> Result<Signature<'static>, Error> {
32         crate::init();
33         let mut ret = ptr::null_mut();
34         let name = CString::new(name)?;
35         let email = CString::new(email)?;
36         unsafe {
37             try_call!(raw::git_signature_now(&mut ret, name, email));
38             Ok(Binding::from_raw(ret))
39         }
40     }
41 
42     /// Create a new action signature.
43     ///
44     /// The `time` specified is in seconds since the epoch, and the `offset` is
45     /// the time zone offset in minutes.
46     ///
47     /// Returns error if either `name` or `email` contain angle brackets.
new(name: &str, email: &str, time: &Time) -> Result<Signature<'static>, Error>48     pub fn new(name: &str, email: &str, time: &Time) -> Result<Signature<'static>, Error> {
49         crate::init();
50         let mut ret = ptr::null_mut();
51         let name = CString::new(name)?;
52         let email = CString::new(email)?;
53         unsafe {
54             try_call!(raw::git_signature_new(
55                 &mut ret,
56                 name,
57                 email,
58                 time.seconds() as raw::git_time_t,
59                 time.offset_minutes() as libc::c_int
60             ));
61             Ok(Binding::from_raw(ret))
62         }
63     }
64 
65     /// Gets the name on the signature.
66     ///
67     /// Returns `None` if the name is not valid utf-8
name(&self) -> Option<&str>68     pub fn name(&self) -> Option<&str> {
69         str::from_utf8(self.name_bytes()).ok()
70     }
71 
72     /// Gets the name on the signature as a byte slice.
name_bytes(&self) -> &[u8]73     pub fn name_bytes(&self) -> &[u8] {
74         unsafe { crate::opt_bytes(self, (*self.raw).name).unwrap() }
75     }
76 
77     /// Gets the email on the signature.
78     ///
79     /// Returns `None` if the email is not valid utf-8
email(&self) -> Option<&str>80     pub fn email(&self) -> Option<&str> {
81         str::from_utf8(self.email_bytes()).ok()
82     }
83 
84     /// Gets the email on the signature as a byte slice.
email_bytes(&self) -> &[u8]85     pub fn email_bytes(&self) -> &[u8] {
86         unsafe { crate::opt_bytes(self, (*self.raw).email).unwrap() }
87     }
88 
89     /// Get the `when` of this signature.
when(&self) -> Time90     pub fn when(&self) -> Time {
91         unsafe { Binding::from_raw((*self.raw).when) }
92     }
93 
94     /// Convert a signature of any lifetime into an owned signature with a
95     /// static lifetime.
to_owned(&self) -> Signature<'static>96     pub fn to_owned(&self) -> Signature<'static> {
97         unsafe {
98             let me = mem::transmute::<&Signature<'a>, &Signature<'static>>(self);
99             me.clone()
100         }
101     }
102 }
103 
104 impl<'a> Binding for Signature<'a> {
105     type Raw = *mut raw::git_signature;
from_raw(raw: *mut raw::git_signature) -> Signature<'a>106     unsafe fn from_raw(raw: *mut raw::git_signature) -> Signature<'a> {
107         Signature {
108             raw,
109             _marker: marker::PhantomData,
110             owned: true,
111         }
112     }
raw(&self) -> *mut raw::git_signature113     fn raw(&self) -> *mut raw::git_signature {
114         self.raw
115     }
116 }
117 
118 /// Creates a new signature from the give raw pointer, tied to the lifetime
119 /// of the given object.
120 ///
121 /// This function is unsafe as there is no guarantee that `raw` is valid for
122 /// `'a` nor if it's a valid pointer.
from_raw_const<'b, T>(_lt: &'b T, raw: *const raw::git_signature) -> Signature<'b>123 pub unsafe fn from_raw_const<'b, T>(_lt: &'b T, raw: *const raw::git_signature) -> Signature<'b> {
124     Signature {
125         raw: raw as *mut raw::git_signature,
126         _marker: marker::PhantomData,
127         owned: false,
128     }
129 }
130 
131 impl Clone for Signature<'static> {
clone(&self) -> Signature<'static>132     fn clone(&self) -> Signature<'static> {
133         // TODO: can this be defined for 'a and just do a plain old copy if the
134         //       lifetime isn't static?
135         let mut raw = ptr::null_mut();
136         let rc = unsafe { raw::git_signature_dup(&mut raw, &*self.raw) };
137         assert_eq!(rc, 0);
138         unsafe { Binding::from_raw(raw) }
139     }
140 }
141 
142 impl<'a> Drop for Signature<'a> {
drop(&mut self)143     fn drop(&mut self) {
144         if self.owned {
145             unsafe { raw::git_signature_free(self.raw) }
146         }
147     }
148 }
149 
150 impl<'a> fmt::Display for Signature<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result151     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152         write!(
153             f,
154             "{} <{}>",
155             String::from_utf8_lossy(self.name_bytes()),
156             String::from_utf8_lossy(self.email_bytes())
157         )
158     }
159 }
160 
161 #[cfg(test)]
162 mod tests {
163     use crate::{Signature, Time};
164 
165     #[test]
smoke()166     fn smoke() {
167         Signature::new("foo", "bar", &Time::new(89, 0)).unwrap();
168         Signature::now("foo", "bar").unwrap();
169         assert!(Signature::new("<foo>", "bar", &Time::new(89, 0)).is_err());
170         assert!(Signature::now("<foo>", "bar").is_err());
171 
172         let s = Signature::now("foo", "bar").unwrap();
173         assert_eq!(s.name(), Some("foo"));
174         assert_eq!(s.email(), Some("bar"));
175 
176         drop(s.clone());
177         drop(s.to_owned());
178     }
179 }
180