client: move matching in login functions to macro

as suggested in PR #84 comments
This commit is contained in:
Johannes Schilling 2018-08-29 15:55:43 +02:00
parent 71f8e6bcd2
commit da2ac87ca7

View file

@ -286,7 +286,6 @@ impl Client<TcpStream> {
ssl_connector: &TlsConnector, ssl_connector: &TlsConnector,
) -> Result<Client<TlsStream<TcpStream>>> { ) -> Result<Client<TlsStream<TcpStream>>> {
// TODO This needs to be tested // TODO This needs to be tested
// TODO(dario): adjust
self.run_command_and_check_ok("STARTTLS")?; self.run_command_and_check_ok("STARTTLS")?;
TlsConnector::connect(ssl_connector, domain, self.conn.stream.into_inner()?) TlsConnector::connect(ssl_connector, domain, self.conn.stream.into_inner()?)
.map(Client::new) .map(Client::new)
@ -294,6 +293,19 @@ impl Client<TcpStream> {
} }
} }
// as the pattern of returning the unauthenticated `Client` back with a login error is relatively
// common, it's abstacted away into a macro here. Note that in theory we wouldn't need the second
// parameter, and could just use the identifier `self` from the surrounding function, but being
// explicit here seems a lot clearer.
macro_rules! ok_or_unauth_client_err {
($r:expr, $self:expr) => {
match $r {
Ok(o) => o,
Err(e) => return Err((e, $self))
}
}
}
impl<T: Read + Write> Client<T> { impl<T: Read + Write> Client<T> {
/// Creates a new client with the underlying stream. /// Creates a new client with the underlying stream.
pub fn new(stream: T) -> Client<T> { pub fn new(stream: T) -> Client<T> {
@ -307,18 +319,15 @@ impl<T: Read + Write> Client<T> {
} }
/// Authenticate will authenticate with the server, using the authenticator given. /// Authenticate will authenticate with the server, using the authenticator given.
pub fn authenticate<A: Authenticator>( pub fn authenticate<A: Authenticator> (
mut self, mut self,
auth_type: &str, auth_type: &str,
authenticator: A, authenticator: A,
) -> ::std::result::Result<Session<T>, (Error, Client<T>)> { ) -> ::std::result::Result<Session<T>, (Error, Client<T>)> {
// explicit match block neccessary to convert error to tuple and not bind self too early // explicit match block neccessary to convert error to tuple and not bind self too early
// (see also comment on `login`) // (see also comment on `login`)
// TODO(dario): macro as suggested in pull/84 ok_or_unauth_client_err!(self.run_command(&format!("AUTHENTICATE {}", auth_type)), self);
match self.run_command(&format!("AUTHENTICATE {}", auth_type)) { self.do_auth_handshake(authenticator)
Ok(_) => self.do_auth_handshake(authenticator),
Err(e) => Err((e, self)),
}
} }
/// This func does the handshake process once the authenticate command is made. /// This func does the handshake process once the authenticate command is made.
@ -331,25 +340,17 @@ impl<T: Read + Write> Client<T> {
let mut line = Vec::new(); let mut line = Vec::new();
// explicit match blocks neccessary to convert error to tuple and not bind self too // explicit match blocks neccessary to convert error to tuple and not bind self too
// early (see also comment on `login`) // early (see also comment on `login`)
if let Err(e) = self.readline(&mut line) { ok_or_unauth_client_err!(self.readline(&mut line), self);
return Err((e, self));
}
if line.starts_with(b"+") { if line.starts_with(b"+") {
let data = match parse_authenticate_response(String::from_utf8(line).unwrap()) { let data = ok_or_unauth_client_err!(
Ok(l) => l, parse_authenticate_response(String::from_utf8(line).unwrap()), self);
Err(e) => return Err((e, self)),
};
let auth_response = authenticator.process(data); let auth_response = authenticator.process(data);
if let Err(e) = self.write_line(auth_response.into_bytes().as_slice()) { ok_or_unauth_client_err!(self.write_line(auth_response.into_bytes().as_slice()), self);
return Err((e, self));
}
} else { } else {
return match self.read_response_onto(&mut line) { ok_or_unauth_client_err!(self.read_response_onto(&mut line), self);
Ok(()) => Ok(Session { conn: self.conn }), return Ok(Session { conn: self.conn });
Err(e) => Err((e, self)),
}
} }
} }
} }
@ -366,20 +367,11 @@ impl<T: Read + Write> Client<T> {
// 2. we can't use `.map_err(|e| (e, self))` because that would capture self into the // 2. we can't use `.map_err(|e| (e, self))` because that would capture self into the
// closure. this way borowck sees that self is only bound in the error case where we // closure. this way borowck sees that self is only bound in the error case where we
// return anyways. // return anyways.
// TODO(dario): macro? let u = ok_or_unauth_client_err!(validate_str(username), self);
let u = match validate_str(username) { let p = ok_or_unauth_client_err!(validate_str(password), self);
Ok(u) => u, ok_or_unauth_client_err!(self.run_command_and_check_ok(&format!("LOGIN {} {}", u, p)), self);
Err(e) => return Err((e, self)),
};
let p = match validate_str(password) {
Ok(p) => p,
Err(e) => return Err((e, self)),
};
match self.run_command_and_check_ok(&format!("LOGIN {} {}", u, p)) { Ok(Session { conn: self.conn })
Ok(()) => Ok(Session { conn: self.conn }),
Err(e) => Err((e, self)),
}
} }
} }