Merge pull request #447 from paolobarbolini/improve
Simplify parts of the code
This commit is contained in:
@@ -112,21 +112,17 @@ impl FromStr for Address {
|
||||
type Err = AddressError;
|
||||
|
||||
fn from_str(val: &str) -> Result<Self, AddressError> {
|
||||
if val.is_empty() || !val.contains('@') {
|
||||
return Err(AddressError::MissingParts);
|
||||
}
|
||||
let mut parts = val.rsplitn(2, '@');
|
||||
let domain = parts.next().ok_or(AddressError::MissingParts)?;
|
||||
let user = parts.next().ok_or(AddressError::MissingParts)?;
|
||||
|
||||
let parts: Vec<&str> = val.rsplitn(2, '@').collect();
|
||||
let user = parts[1];
|
||||
let domain = parts[0];
|
||||
|
||||
Address::check_user(user)
|
||||
.and_then(|_| Address::check_domain(domain))
|
||||
.map(|_| Address {
|
||||
user: user.into(),
|
||||
domain: domain.into(),
|
||||
complete: val.to_string(),
|
||||
})
|
||||
Address::check_user(user)?;
|
||||
Address::check_domain(domain)?;
|
||||
Ok(Address {
|
||||
user: user.into(),
|
||||
domain: domain.into(),
|
||||
complete: val.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
src/error.rs
24
src/error.rs
@@ -28,18 +28,18 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fmt.write_str(&match self {
|
||||
Error::MissingFrom => "missing source address, invalid envelope".to_string(),
|
||||
Error::MissingTo => "missing destination address, invalid envelope".to_string(),
|
||||
Error::TooManyFrom => "there can only be one source address".to_string(),
|
||||
Error::EmailMissingAt => "missing @ in email address".to_string(),
|
||||
Error::EmailMissingLocalPart => "missing local part in email address".to_string(),
|
||||
Error::EmailMissingDomain => "missing domain in email address".to_string(),
|
||||
Error::CannotParseFilename => "could not parse attachment filename".to_string(),
|
||||
Error::NonAsciiChars => "contains non-ASCII chars".to_string(),
|
||||
Error::Io(e) => e.to_string(),
|
||||
})
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
Error::MissingFrom => f.write_str("missing source address, invalid envelope"),
|
||||
Error::MissingTo => f.write_str("missing destination address, invalid envelope"),
|
||||
Error::TooManyFrom => f.write_str("there can only be one source address"),
|
||||
Error::EmailMissingAt => f.write_str("missing @ in email address"),
|
||||
Error::EmailMissingLocalPart => f.write_str("missing local part in email address"),
|
||||
Error::EmailMissingDomain => f.write_str("missing domain in email address"),
|
||||
Error::CannotParseFilename => f.write_str("could not parse attachment filename"),
|
||||
Error::NonAsciiChars => f.write_str("contains non-ASCII chars"),
|
||||
Error::Io(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,15 +35,12 @@ impl Header for MimeVersion {
|
||||
Self: Sized,
|
||||
{
|
||||
raw.one().ok_or(HeaderError::Header).and_then(|r| {
|
||||
let s: Vec<&str> = from_utf8(r)
|
||||
.map_err(|_| HeaderError::Header)?
|
||||
.split('.')
|
||||
.collect();
|
||||
if s.len() != 2 {
|
||||
return Err(HeaderError::Header);
|
||||
}
|
||||
let major = s[0].parse().map_err(|_| HeaderError::Header)?;
|
||||
let minor = s[1].parse().map_err(|_| HeaderError::Header)?;
|
||||
let mut s = from_utf8(r).map_err(|_| HeaderError::Header)?.split('.');
|
||||
|
||||
let major = s.next().ok_or(HeaderError::Header)?;
|
||||
let minor = s.next().ok_or(HeaderError::Header)?;
|
||||
let major = major.parse().map_err(|_| HeaderError::Header)?;
|
||||
let minor = minor.parse().map_err(|_| HeaderError::Header)?;
|
||||
Ok(MimeVersion::new(major, minor))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -209,10 +209,8 @@ fn make_boundary() -> String {
|
||||
}
|
||||
|
||||
impl MultiPartKind {
|
||||
fn to_mime<S: AsRef<str>>(&self, boundary: Option<S>) -> Mime {
|
||||
let boundary = boundary
|
||||
.map(|s| s.as_ref().into())
|
||||
.unwrap_or_else(make_boundary);
|
||||
fn to_mime<S: Into<String>>(&self, boundary: Option<S>) -> Mime {
|
||||
let boundary = boundary.map_or_else(make_boundary, |s| s.into());
|
||||
|
||||
use self::MultiPartKind::*;
|
||||
format!(
|
||||
|
||||
@@ -23,15 +23,8 @@ pub fn decode(s: &str) -> Option<String> {
|
||||
let s = s.split_at(10).1;
|
||||
let s = s.split_at(s.len() - 2).0;
|
||||
base64::decode(s)
|
||||
.map_err(|_| ())
|
||||
.and_then(|v| {
|
||||
if let Ok(s) = from_utf8(&v) {
|
||||
Ok(Some(s.into()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
.unwrap_or(None)
|
||||
.ok()
|
||||
.and_then(|v| from_utf8(&v).ok().map(|s| s.into()))
|
||||
} else {
|
||||
Some(s.into())
|
||||
}
|
||||
|
||||
@@ -62,15 +62,11 @@ pub enum Mechanism {
|
||||
|
||||
impl Display for Mechanism {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match *self {
|
||||
Mechanism::Plain => "PLAIN",
|
||||
Mechanism::Login => "LOGIN",
|
||||
Mechanism::Xoauth2 => "XOAUTH2",
|
||||
}
|
||||
)
|
||||
f.write_str(match *self {
|
||||
Mechanism::Plain => "PLAIN",
|
||||
Mechanism::Login => "LOGIN",
|
||||
Mechanism::Xoauth2 => "XOAUTH2",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ impl ClientCodec {
|
||||
}
|
||||
|
||||
/// Adds transparency
|
||||
fn encode(&mut self, frame: &[u8], buf: &mut Vec<u8>) -> Result<(), Error> {
|
||||
fn encode(&mut self, frame: &[u8], buf: &mut Vec<u8>) {
|
||||
match frame.len() {
|
||||
0 => {
|
||||
match self.escape_count {
|
||||
@@ -50,7 +50,6 @@ impl ClientCodec {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
self.escape_count = 0;
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
let mut start = 0;
|
||||
@@ -69,7 +68,6 @@ impl ClientCodec {
|
||||
}
|
||||
}
|
||||
buf.extend_from_slice(&frame[start..]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,7 +281,7 @@ impl SmtpConnection {
|
||||
pub fn message(&mut self, message: &[u8]) -> Result<Response, Error> {
|
||||
let mut out_buf: Vec<u8> = vec![];
|
||||
let mut codec = ClientCodec::new();
|
||||
codec.encode(message, &mut out_buf)?;
|
||||
codec.encode(message, &mut out_buf);
|
||||
self.write(out_buf.as_slice())?;
|
||||
self.write(b"\r\n.\r\n")?;
|
||||
self.read_response()
|
||||
@@ -346,15 +344,15 @@ mod test {
|
||||
let mut codec = ClientCodec::new();
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
|
||||
assert!(codec.encode(b"test\r\n", &mut buf).is_ok());
|
||||
assert!(codec.encode(b".\r\n", &mut buf).is_ok());
|
||||
assert!(codec.encode(b"\r\ntest", &mut buf).is_ok());
|
||||
assert!(codec.encode(b"te\r\n.\r\nst", &mut buf).is_ok());
|
||||
assert!(codec.encode(b"test", &mut buf).is_ok());
|
||||
assert!(codec.encode(b"test.", &mut buf).is_ok());
|
||||
assert!(codec.encode(b"test\n", &mut buf).is_ok());
|
||||
assert!(codec.encode(b".test\n", &mut buf).is_ok());
|
||||
assert!(codec.encode(b"test", &mut buf).is_ok());
|
||||
codec.encode(b"test\r\n", &mut buf);
|
||||
codec.encode(b".\r\n", &mut buf);
|
||||
codec.encode(b"\r\ntest", &mut buf);
|
||||
codec.encode(b"te\r\n.\r\nst", &mut buf);
|
||||
codec.encode(b"test", &mut buf);
|
||||
codec.encode(b"test.", &mut buf);
|
||||
codec.encode(b"test\n", &mut buf);
|
||||
codec.encode(b".test\n", &mut buf);
|
||||
codec.encode(b"test", &mut buf);
|
||||
assert_eq!(
|
||||
String::from_utf8(buf).unwrap(),
|
||||
"test\r\n..\r\n\r\ntestte\r\n..\r\nsttesttest.test\n.test\ntest"
|
||||
|
||||
@@ -99,9 +99,8 @@ impl NetworkStream {
|
||||
) -> Result<TcpStream, Error> {
|
||||
let addrs = server.to_socket_addrs()?;
|
||||
for addr in addrs {
|
||||
let result = TcpStream::connect_timeout(&addr, timeout);
|
||||
if result.is_ok() {
|
||||
return result.map_err(|e| e.into());
|
||||
if let Ok(result) = TcpStream::connect_timeout(&addr, timeout) {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
Err(Error::Client("Could not connect"))
|
||||
|
||||
@@ -147,8 +147,8 @@ pub struct Help {
|
||||
impl Display for Help {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_str("HELP")?;
|
||||
if self.argument.is_some() {
|
||||
write!(f, " {}", self.argument.as_ref().unwrap())?;
|
||||
if let Some(argument) = &self.argument {
|
||||
write!(f, " {}", argument)?;
|
||||
}
|
||||
f.write_str("\r\n")
|
||||
}
|
||||
|
||||
@@ -43,18 +43,17 @@ impl ClientId {
|
||||
|
||||
/// Defines a `ClientId` with the current hostname, of `localhost` if hostname could not be
|
||||
/// found
|
||||
#[cfg(feature = "hostname")]
|
||||
pub fn hostname() -> ClientId {
|
||||
ClientId::Domain(
|
||||
#[cfg(feature = "hostname")]
|
||||
return ClientId::Domain(
|
||||
hostname::get()
|
||||
.map_err(|_| ())
|
||||
.and_then(|s| s.into_string().map_err(|_| ()))
|
||||
.unwrap_or_else(|_| DEFAULT_DOMAIN_CLIENT_ID.to_string()),
|
||||
)
|
||||
}
|
||||
#[cfg(not(feature = "hostname"))]
|
||||
pub fn hostname() -> ClientId {
|
||||
ClientId::Domain(DEFAULT_DOMAIN_CLIENT_ID.to_string())
|
||||
.ok()
|
||||
.and_then(|s| s.into_string().ok())
|
||||
.unwrap_or_else(|| DEFAULT_DOMAIN_CLIENT_ID.to_string()),
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "hostname"))]
|
||||
return ClientId::Domain(DEFAULT_DOMAIN_CLIENT_ID.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +80,9 @@ pub enum Extension {
|
||||
impl Display for Extension {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Extension::EightBitMime => write!(f, "8BITMIME"),
|
||||
Extension::SmtpUtfEight => write!(f, "SMTPUTF8"),
|
||||
Extension::StartTls => write!(f, "STARTTLS"),
|
||||
Extension::EightBitMime => f.write_str("8BITMIME"),
|
||||
Extension::SmtpUtfEight => f.write_str("SMTPUTF8"),
|
||||
Extension::StartTls => f.write_str("STARTTLS"),
|
||||
Extension::Authentication(ref mechanism) => write!(f, "AUTH {}", mechanism),
|
||||
}
|
||||
}
|
||||
@@ -105,16 +104,12 @@ pub struct ServerInfo {
|
||||
|
||||
impl Display for ServerInfo {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} with {}",
|
||||
self.name,
|
||||
if self.features.is_empty() {
|
||||
"no supported features".to_string()
|
||||
} else {
|
||||
format!("{:?}", self.features)
|
||||
}
|
||||
)
|
||||
let features = if self.features.is_empty() {
|
||||
"no supported features".to_string()
|
||||
} else {
|
||||
format!("{:?}", self.features)
|
||||
};
|
||||
write!(f, "{} with {}", self.name, features)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -237,25 +237,19 @@ pub(crate) fn parse_response(i: &str) -> IResult<&str, Response> {
|
||||
let (i, _) = complete(tag("\r\n"))(i)?;
|
||||
|
||||
// Check that all codes are equal.
|
||||
if !lines.iter().all(|&(ref code, _, _)| *code == last_code) {
|
||||
if !lines.iter().all(|&(code, _, _)| code == last_code) {
|
||||
return Err(nom::Err::Failure(("", nom::error::ErrorKind::Not)));
|
||||
}
|
||||
|
||||
// Extract text from lines, and append last line.
|
||||
let mut lines: Vec<&str> = lines
|
||||
.into_iter()
|
||||
.map(|(_, text, _)| text)
|
||||
.collect::<Vec<_>>();
|
||||
lines.push(last_line);
|
||||
let mut lines: Vec<String> = lines.into_iter().map(|(_, text, _)| text.into()).collect();
|
||||
lines.push(last_line.into());
|
||||
|
||||
Ok((
|
||||
i,
|
||||
Response {
|
||||
code: last_code,
|
||||
message: lines
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>(),
|
||||
message: lines,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@@ -36,15 +36,11 @@ pub struct Error;
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "stub error")
|
||||
f.write_str("stub error")
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl StdError for Error {}
|
||||
|
||||
/// This transport logs the message envelope and returns the given response
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
||||
Reference in New Issue
Block a user