Skip to main content

gix_actor/signature/
mod.rs

1mod _ref {
2    use bstr::ByteSlice;
3
4    use crate::{signature::decode, IdentityRef, Signature, SignatureRef};
5
6    /// Lifecycle
7    impl<'a> SignatureRef<'a> {
8        /// Deserialize a signature from the given `data`.
9        ///
10        /// Typical input is `Name <name@example.com> 1700000000 +0000`.
11        pub fn from_bytes(mut data: &'a [u8]) -> Result<SignatureRef<'a>, gix_error::ValidationError> {
12            Self::from_bytes_consuming(&mut data)
13        }
14
15        /// Deserialize a signature from the given `data` and advance it past the signature.
16        ///
17        /// Typical input is `Name <name@example.com> 1700000000 +0000`; on
18        /// success, `data` points to the bytes immediately after the parsed
19        /// signature.
20        pub fn from_bytes_consuming(data: &mut &'a [u8]) -> Result<SignatureRef<'a>, gix_error::ValidationError> {
21            decode(data)
22        }
23
24        /// Try to parse the timestamp and create an owned instance from this shared one.
25        pub fn to_owned(&self) -> Result<Signature, gix_date::Error> {
26            Ok(Signature {
27                name: self.name.to_owned(),
28                email: self.email.to_owned(),
29                time: self.time()?,
30            })
31        }
32    }
33
34    /// Access
35    impl<'a> SignatureRef<'a> {
36        /// Trim the whitespace surrounding the `name`, `email` and `time` and return a new signature.
37        pub fn trim(&self) -> SignatureRef<'a> {
38            SignatureRef {
39                name: self.name.trim().as_bstr(),
40                email: self.email.trim().as_bstr(),
41                time: self.time.trim(),
42            }
43        }
44
45        /// Return the actor's name and email, effectively excluding the timestamp of this signature.
46        pub fn actor(&self) -> IdentityRef<'a> {
47            IdentityRef {
48                name: self.name,
49                email: self.email,
50            }
51        }
52
53        /// Parse only the seconds since unix epoch from the `time` field, or silently default to 0
54        /// if parsing fails. Note that this ignores the timezone, so it can parse otherwise broken dates.
55        ///
56        /// For a fallible and more complete, but slower version, use [`time()`](Self::time).
57        pub fn seconds(&self) -> gix_date::SecondsSinceUnixEpoch {
58            self.time
59                .trim()
60                .split(' ')
61                .next()
62                .and_then(|i| i.parse().ok())
63                .unwrap_or_default()
64        }
65
66        /// Parse the `time` field for access to the passed time since unix epoch, and the time offset.
67        /// The format is expected to be [raw](gix_date::parse_header()).
68        pub fn time(&self) -> Result<gix_date::Time, gix_date::Error> {
69            self.time.parse()
70        }
71    }
72}
73
74mod convert {
75    use gix_date::parse::TimeBuf;
76
77    use crate::{Signature, SignatureRef};
78
79    impl Signature {
80        /// Borrow this instance as immutable, serializing the `time` field into `buf`.
81        ///
82        /// Commonly used as [`signature.to_ref(&mut TimeBuf::default())`](TimeBuf::default).
83        pub fn to_ref<'a>(&'a self, time_buf: &'a mut TimeBuf) -> SignatureRef<'a> {
84            SignatureRef {
85                name: self.name.as_ref(),
86                email: self.email.as_ref(),
87                time: self.time.to_str(time_buf),
88            }
89        }
90    }
91
92    /// Note that this conversion is lossy due to the lenient parsing of the [`time`](SignatureRef::time) field.
93    impl From<SignatureRef<'_>> for Signature {
94        fn from(other: SignatureRef<'_>) -> Signature {
95            Signature {
96                name: other.name.to_owned(),
97                email: other.email.to_owned(),
98                time: other.time().unwrap_or_default(),
99            }
100        }
101    }
102}
103
104pub(crate) mod write {
105    use bstr::{BStr, ByteSlice};
106    use gix_date::parse::TimeBuf;
107
108    use crate::{Signature, SignatureRef};
109
110    /// Output
111    impl Signature {
112        /// Serialize this instance to `out` in the git serialization format for actors.
113        pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {
114            let mut buf = TimeBuf::default();
115            self.to_ref(&mut buf).write_to(out)
116        }
117        /// Computes the number of bytes necessary to serialize this signature
118        pub fn size(&self) -> usize {
119            self.name.len() + 2 /* space <*/ + self.email.len() +  2 /* > space */ + self.time.size()
120        }
121    }
122
123    impl SignatureRef<'_> {
124        /// Serialize this instance to `out` in the git serialization format for actors.
125        pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {
126            out.write_all(validated_token(self.name).map_err(std::io::Error::other)?)?;
127            out.write_all(b" ")?;
128            out.write_all(b"<")?;
129            out.write_all(validated_token(self.email).map_err(std::io::Error::other)?)?;
130            out.write_all(b"> ")?;
131            out.write_all(validated_token(self.time.into()).map_err(std::io::Error::other)?)
132        }
133        /// Computes the number of bytes necessary to serialize this signature
134        pub fn size(&self) -> usize {
135            self.name.len() + 2 /* space <*/ + self.email.len() +  2 /* > space */ + self.time.len()
136        }
137    }
138
139    pub(crate) fn validated_token(name: &BStr) -> Result<&BStr, gix_error::ValidationError> {
140        if name.find_byteset(b"<>\n").is_some() {
141            return Err(gix_error::ValidationError::new_with_input(
142                "Signature name or email must not contain '<', '>' or \\n",
143                name,
144            ));
145        }
146        Ok(name)
147    }
148}
149
150///
151pub mod decode;
152pub use decode::function::decode;