1use std::ffi::CString;
2use std::fmt;
3use std::marker;
4use std::mem;
5use std::ptr;
6use std::str;
7
8use crate::util::Binding;
9use crate::{raw, Error, Time};
10
11pub struct Signature<'a> {
21 raw: *mut raw::git_signature,
22 _marker: marker::PhantomData<&'a str>,
23 owned: bool,
24}
25
26impl<'a> Signature<'a> {
27 pub fn now(name: &str, email: &str) -> Result<Signature<'static>, Error> {
31 crate::init();
32 let mut ret = ptr::null_mut();
33 let name = CString::new(name)?;
34 let email = CString::new(email)?;
35 unsafe {
36 try_call!(raw::git_signature_now(&mut ret, name, email));
37 Ok(Binding::from_raw(ret))
38 }
39 }
40
41 pub fn new(name: &str, email: &str, time: &Time) -> Result<Signature<'static>, Error> {
48 crate::init();
49 let mut ret = ptr::null_mut();
50 let name = CString::new(name)?;
51 let email = CString::new(email)?;
52 unsafe {
53 try_call!(raw::git_signature_new(
54 &mut ret,
55 name,
56 email,
57 time.seconds() as raw::git_time_t,
58 time.offset_minutes() as libc::c_int
59 ));
60 Ok(Binding::from_raw(ret))
61 }
62 }
63
64 pub fn name(&self) -> Option<&str> {
68 str::from_utf8(self.name_bytes()).ok()
69 }
70
71 pub fn name_bytes(&self) -> &[u8] {
73 unsafe { crate::opt_bytes(self, (*self.raw).name).unwrap() }
74 }
75
76 pub fn email(&self) -> Option<&str> {
80 str::from_utf8(self.email_bytes()).ok()
81 }
82
83 pub fn email_bytes(&self) -> &[u8] {
85 unsafe { crate::opt_bytes(self, (*self.raw).email).unwrap() }
86 }
87
88 pub fn when(&self) -> Time {
90 unsafe { Binding::from_raw((*self.raw).when) }
91 }
92
93 pub fn to_owned(&self) -> Signature<'static> {
96 unsafe {
97 let me = mem::transmute::<&Signature<'a>, &Signature<'static>>(self);
98 me.clone()
99 }
100 }
101}
102
103impl<'a> Binding for Signature<'a> {
104 type Raw = *mut raw::git_signature;
105 unsafe fn from_raw(raw: *mut raw::git_signature) -> Signature<'a> {
106 Signature {
107 raw,
108 _marker: marker::PhantomData,
109 owned: true,
110 }
111 }
112 fn raw(&self) -> *mut raw::git_signature {
113 self.raw
114 }
115}
116
117pub unsafe fn from_raw_const<'b, T>(_lt: &'b T, raw: *const raw::git_signature) -> Signature<'b> {
123 Signature {
124 raw: raw as *mut raw::git_signature,
125 _marker: marker::PhantomData,
126 owned: false,
127 }
128}
129
130impl Clone for Signature<'static> {
131 fn clone(&self) -> Signature<'static> {
132 let mut raw = ptr::null_mut();
135 let rc = unsafe { raw::git_signature_dup(&mut raw, &*self.raw) };
136 assert_eq!(rc, 0);
137 unsafe { Binding::from_raw(raw) }
138 }
139}
140
141impl<'a> Drop for Signature<'a> {
142 fn drop(&mut self) {
143 if self.owned {
144 unsafe { raw::git_signature_free(self.raw) }
145 }
146 }
147}
148
149impl<'a> fmt::Display for Signature<'a> {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(
152 f,
153 "{} <{}>",
154 String::from_utf8_lossy(self.name_bytes()),
155 String::from_utf8_lossy(self.email_bytes())
156 )
157 }
158}
159
160impl PartialEq for Signature<'_> {
161 fn eq(&self, other: &Self) -> bool {
162 self.when() == other.when()
163 && self.email_bytes() == other.email_bytes()
164 && self.name_bytes() == other.name_bytes()
165 }
166}
167
168impl Eq for Signature<'_> {}
169
170#[cfg(test)]
171mod tests {
172 use crate::{Signature, Time};
173
174 #[test]
175 fn smoke() {
176 Signature::new("foo", "bar", &Time::new(89, 0)).unwrap();
177 Signature::now("foo", "bar").unwrap();
178 assert!(Signature::new("<foo>", "bar", &Time::new(89, 0)).is_err());
179 assert!(Signature::now("<foo>", "bar").is_err());
180
181 let s = Signature::now("foo", "bar").unwrap();
182 assert_eq!(s.name(), Some("foo"));
183 assert_eq!(s.email(), Some("bar"));
184
185 drop(s.clone());
186 drop(s.to_owned());
187 }
188}