4 /// The error returned by [`parse()`][crate::parse()].
5 #[derive(Debug, thiserror::Error)]
8 #[error("Line {line_number} has too many names or emails, or none at all: {line:?}")]
9 UnconsumedInput { line_number: usize, line: BString }
,
10 #[error("{line_number}: {line:?}: {message}")]
19 use bstr
::{BStr, ByteSlice}
;
24 /// An iterator to parse mailmap lines on-demand.
25 pub struct Lines
<'a
> {
26 lines
: bstr
::Lines
<'a
>,
31 pub(crate) fn new(input
: &'a
[u8]) -> Self {
33 lines
: input
.as_bstr().lines(),
39 impl<'a
> Iterator
for Lines
<'a
> {
40 type Item
= Result
<Entry
<'a
>, Error
>;
42 fn next(&mut self) -> Option
<Self::Item
> {
43 for line
in self.lines
.by_ref() {
47 Some(b
) if *b
== b'
#' => continue,
50 let line
= line
.trim();
54 return parse_line(line
.into(), self.line_no
).into();
60 fn parse_line(line
: &BStr
, line_number
: usize) -> Result
<Entry
<'_
>, Error
> {
61 let (name1
, email1
, rest
) = parse_name_and_email(line
, line_number
)?
;
62 let (name2
, email2
, rest
) = parse_name_and_email(rest
, line_number
)?
;
63 if !rest
.trim().is_empty() {
64 return Err(Error
::UnconsumedInput
{
69 Ok(match (name1
, email1
, name2
, email2
) {
70 (Some(proper_name
), Some(commit_email
), None
, None
) => Entry
::change_name_by_email(proper_name
, commit_email
),
71 (None
, Some(proper_email
), None
, Some(commit_email
)) => {
72 Entry
::change_email_by_email(proper_email
, commit_email
)
74 (Some(proper_name
), Some(proper_email
), None
, Some(commit_email
)) => {
75 Entry
::change_name_and_email_by_email(proper_name
, proper_email
, commit_email
)
77 (Some(proper_name
), Some(proper_email
), Some(commit_name
), Some(commit_email
)) => {
78 Entry
::change_name_and_email_by_name_and_email(proper_name
, proper_email
, commit_name
, commit_email
)
81 return Err(Error
::Malformed
{
84 message
: "Emails without a name or email to map to are invalid".into(),
90 fn parse_name_and_email(
93 ) -> Result
<(Option
<&'_ BStr
>, Option
<&'_ BStr
>, &'_ BStr
), Error
> {
94 match line
.find_byte(b'
<'
) {
95 Some(start_bracket
) => {
96 let email
= &line
[start_bracket
+ 1..];
97 let closing_bracket
= email
.find_byte(b'
>'
).ok_or_else(|| Error
::Malformed
{
100 message
: "Missing closing bracket '>' in email".into(),
102 let email
= email
[..closing_bracket
].trim().as_bstr();
103 if email
.is_empty() {
104 return Err(Error
::Malformed
{
107 message
: "Email must not be empty".into(),
110 let name
= line
[..start_bracket
].trim().as_bstr();
111 let rest
= line
[start_bracket
+ closing_bracket
+ 2..].as_bstr();
112 Ok(((!name
.is_empty()).then_some(name
), Some(email
), rest
))
114 None
=> Ok((None
, None
, line
)),