]> git.proxmox.com Git - rustc.git/blame - vendor/gix-actor/src/signature/decode.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / gix-actor / src / signature / decode.rs
CommitLineData
fe692bf9
FG
1pub(crate) mod function {
2 use bstr::ByteSlice;
3 use btoi::btoi;
781aab86
FG
4 use gix_date::{time::Sign, OffsetInSeconds, SecondsSinceUnixEpoch, Time};
5 use winnow::{
6 combinator::{alt, separated_pair, terminated},
7 error::{AddContext, ParserError, StrContext},
8 prelude::*,
9 stream::AsChar,
10 token::{take, take_until0, take_while},
fe692bf9
FG
11 };
12
781aab86 13 use crate::{IdentityRef, SignatureRef};
fe692bf9
FG
14
15 const SPACE: &[u8] = b" ";
16
17 /// Parse a signature from the bytes input `i` using `nom`.
781aab86
FG
18 pub fn decode<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>(
19 i: &mut &'a [u8],
20 ) -> PResult<SignatureRef<'a>, E> {
21 separated_pair(
22 identity,
23 b" ",
24 (
25 terminated(take_until0(SPACE), take(1usize))
26 .verify_map(|v| btoi::<SecondsSinceUnixEpoch>(v).ok())
27 .context(StrContext::Expected("<timestamp>".into())),
28 alt((
29 take_while(1.., b'-').map(|_| Sign::Minus),
30 take_while(1.., b'+').map(|_| Sign::Plus),
31 ))
32 .context(StrContext::Expected("+|-".into())),
33 take_while(2, AsChar::is_dec_digit)
34 .verify_map(|v| btoi::<OffsetInSeconds>(v).ok())
35 .context(StrContext::Expected("HH".into())),
36 take_while(1..=2, AsChar::is_dec_digit)
37 .verify_map(|v| btoi::<OffsetInSeconds>(v).ok())
38 .context(StrContext::Expected("MM".into())),
39 )
40 .map(|(time, sign, hours, minutes)| {
41 let offset = (hours * 3600 + minutes * 60) * if sign == Sign::Minus { -1 } else { 1 };
42 Time {
43 seconds: time,
44 offset,
45 sign,
46 }
fe692bf9 47 }),
781aab86
FG
48 )
49 .context(StrContext::Expected("<name> <<email>> <timestamp> <+|-><HHMM>".into()))
50 .map(|(identity, time)| SignatureRef {
51 name: identity.name,
52 email: identity.email,
53 time,
54 })
55 .parse_next(i)
fe692bf9
FG
56 }
57
58 /// Parse an identity from the bytes input `i` (like `name <email>`) using `nom`.
781aab86
FG
59 pub fn identity<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>(
60 i: &mut &'a [u8],
61 ) -> PResult<IdentityRef<'a>, E> {
62 (
63 terminated(take_until0(&b" <"[..]), take(2usize)).context(StrContext::Expected("<name>".into())),
64 terminated(take_until0(&b">"[..]), take(1usize)).context(StrContext::Expected("<email>".into())),
65 )
66 .map(|(name, email): (&[u8], &[u8])| IdentityRef {
fe692bf9
FG
67 name: name.as_bstr(),
68 email: email.as_bstr(),
781aab86
FG
69 })
70 .context(StrContext::Expected("<name> <<email>>".into()))
71 .parse_next(i)
fe692bf9 72 }
0a29b90c 73}
fe692bf9 74pub use function::identity;
0a29b90c
FG
75
76#[cfg(test)]
77mod tests {
78 mod parse_signature {
79 use bstr::ByteSlice;
781aab86 80 use gix_date::{time::Sign, OffsetInSeconds, SecondsSinceUnixEpoch};
0a29b90c 81 use gix_testtools::to_bstr_err;
781aab86 82 use winnow::prelude::*;
0a29b90c 83
781aab86 84 use crate::{signature, SignatureRef, Time};
0a29b90c 85
781aab86
FG
86 fn decode<'i>(
87 i: &mut &'i [u8],
88 ) -> PResult<SignatureRef<'i>, winnow::error::TreeError<&'i [u8], winnow::error::StrContext>> {
89 signature::decode.parse_next(i)
0a29b90c
FG
90 }
91
92 fn signature(
93 name: &'static str,
94 email: &'static str,
781aab86 95 seconds: SecondsSinceUnixEpoch,
0a29b90c 96 sign: Sign,
781aab86 97 offset: OffsetInSeconds,
0a29b90c
FG
98 ) -> SignatureRef<'static> {
99 SignatureRef {
100 name: name.as_bytes().as_bstr(),
101 email: email.as_bytes().as_bstr(),
781aab86 102 time: Time { seconds, offset, sign },
0a29b90c
FG
103 }
104 }
105
106 #[test]
107 fn tz_minus() {
108 assert_eq!(
781aab86
FG
109 decode
110 .parse_peek(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 -0230")
0a29b90c
FG
111 .expect("parse to work")
112 .1,
113 signature("Sebastian Thiel", "byronimo@gmail.com", 1528473343, Sign::Minus, -9000)
114 );
115 }
116
117 #[test]
118 fn tz_plus() {
119 assert_eq!(
781aab86
FG
120 decode
121 .parse_peek(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 +0230")
0a29b90c
FG
122 .expect("parse to work")
123 .1,
124 signature("Sebastian Thiel", "byronimo@gmail.com", 1528473343, Sign::Plus, 9000)
125 );
126 }
127
128 #[test]
129 fn negative_offset_0000() {
130 assert_eq!(
781aab86
FG
131 decode
132 .parse_peek(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 -0000")
0a29b90c
FG
133 .expect("parse to work")
134 .1,
135 signature("Sebastian Thiel", "byronimo@gmail.com", 1528473343, Sign::Minus, 0)
136 );
137 }
138
781aab86
FG
139 #[test]
140 fn negative_offset_double_dash() {
141 assert_eq!(
142 decode
143 .parse_peek(b"name <name@example.com> 1288373970 --700")
144 .expect("parse to work")
145 .1,
146 signature("name", "name@example.com", 1288373970, Sign::Minus, -252000)
147 );
148 }
149
0a29b90c
FG
150 #[test]
151 fn empty_name_and_email() {
152 assert_eq!(
781aab86 153 decode.parse_peek(b" <> 12345 -1215").expect("parse to work").1,
0a29b90c
FG
154 signature("", "", 12345, Sign::Minus, -44100)
155 );
156 }
157
158 #[test]
159 fn invalid_signature() {
160 assert_eq!(
781aab86 161 decode.parse_peek(b"hello < 12345 -1215")
0a29b90c
FG
162 .map_err(to_bstr_err)
163 .expect_err("parse fails as > is missing")
164 .to_string(),
781aab86 165 "in slice at ' 12345 -1215'\n 0: expected `<email>` at ' 12345 -1215'\n 1: expected `<name> <<email>>` at ' 12345 -1215'\n 2: expected `<name> <<email>> <timestamp> <+|-><HHMM>` at ' 12345 -1215'\n"
0a29b90c
FG
166 );
167 }
168
169 #[test]
170 fn invalid_time() {
171 assert_eq!(
781aab86 172 decode.parse_peek(b"hello <> abc -1215")
0a29b90c
FG
173 .map_err(to_bstr_err)
174 .expect_err("parse fails as > is missing")
175 .to_string(),
781aab86 176 "in predicate verification at 'abc -1215'\n 0: expected `<timestamp>` at 'abc -1215'\n 1: expected `<name> <<email>> <timestamp> <+|-><HHMM>` at 'abc -1215'\n"
0a29b90c
FG
177 );
178 }
179 }
180}