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