Rust upgrade.

This commit is contained in:
Artemciy
2015-02-27 18:18:15 +03:00
committed by DevOps
parent 96487a1937
commit aaf2147117
15 changed files with 145 additions and 137 deletions

View File

@@ -11,9 +11,9 @@
//! TODO
use std::io::IoResult;
use std::io::net::ip::SocketAddr;
use std::io::net::tcp::TcpStream;
use std::old_io::IoResult;
use std::old_io::net::ip::SocketAddr;
use std::old_io::net::tcp::TcpStream;
/// A trait for the concept of opening a stream connected to a IP socket address.
pub trait Connecter {

View File

@@ -9,10 +9,11 @@
//! SMTP client
use std::ascii::AsciiExt;
use std::string::String;
use std::error::FromError;
use std::io::net::tcp::TcpStream;
use std::io::net::ip::{SocketAddr, ToSocketAddr};
use std::old_io::net::tcp::TcpStream;
use std::old_io::net::ip::{SocketAddr, ToSocketAddr};
use uuid::Uuid;
@@ -33,18 +34,18 @@ pub mod connecter;
pub mod stream;
/// Represents the configuration of a client
#[deriving(Clone)]
#[derive(Clone,Debug)]
pub struct Configuration {
/// Maximum connection reuse
///
/// Zero means no limitation
pub connection_reuse_count_limit: uint,
pub connection_reuse_count_limit: usize,
/// Enable connection reuse
pub enable_connection_reuse: bool,
/// Maximum recipients
pub destination_recipient_limit: uint,
pub destination_recipient_limit: usize,
/// Maximum line length
pub line_length_limit: uint,
pub line_length_limit: usize,
/// Name sent during HELO or EHLO
pub hello_name: String,
}
@@ -64,7 +65,7 @@ pub struct Client<S = TcpStream> {
/// Panic state
panic: bool,
/// Connection reuse counter
connection_reuse_count: uint,
connection_reuse_count: usize,
/// Current message id
current_message: Option<Uuid>,
/// Configuration of the client
@@ -72,16 +73,16 @@ pub struct Client<S = TcpStream> {
}
macro_rules! try_smtp (
($err: expr $client: ident) => ({
($err: expr, $client: ident) => ({
match $err {
Ok(val) => val,
Err(err) => close_and_return_err!(err $client),
Err(err) => close_and_return_err!(err, $client),
}
})
);
macro_rules! close_and_return_err (
($err: expr $client: ident) => ({
($err: expr, $client: ident) => ({
if !$client.panic {
$client.panic = true;
$client.close();
@@ -94,7 +95,7 @@ macro_rules! check_command_sequence (
($command: ident $client: ident) => ({
if !$client.state.is_allowed(&$command) {
close_and_return_err!(
Response{code: 503, message: Some("Bad sequence of commands".to_string())} $client
Response{code: 503, message: Some("Bad sequence of commands".to_string())}, $client
);
}
})
@@ -141,7 +142,7 @@ impl<S = TcpStream> Client<S> {
}
/// Set the maximum number of emails sent using one connection
pub fn set_connection_reuse_count_limit(&mut self, count: uint) {
pub fn set_connection_reuse_count_limit(&mut self, count: usize) {
self.configuration.connection_reuse_count_limit = count
}
@@ -189,16 +190,16 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
if let Err(error) = self.ehlo() {
match error.kind {
ErrorKind::PermanentError(Response{code: 550, message: _}) => {
try_smtp!(self.helo() self);
try_smtp!(self.helo(), self);
},
_ => {
try_smtp!(Err(error) self)
try_smtp!(Err(error), self)
},
};
}
// Print server information
debug!("server {}", self.server_info.as_ref().unwrap());
//debug!("server {}", self.server_info.as_ref().unwrap());
}
self.current_message = Some(Uuid::new_v4());
@@ -210,17 +211,17 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
let message = email.message();
// Mail
try_smtp!(self.mail(from_address.as_slice()) self);
try_smtp!(self.mail(from_address.as_slice()), self);
// Recipient
// TODO Return rejected addresses
// TODO Limit the number of recipients
for to_address in to_addresses.iter() {
try_smtp!(self.rcpt(to_address.as_slice()) self);
try_smtp!(self.rcpt(to_address.as_slice()), self);
}
// Data
try_smtp!(self.data() self);
try_smtp!(self.data(), self);
// Message content
self.message(message.as_slice())
@@ -234,15 +235,15 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
// Connect should not be called when the client is already connected
if self.stream.is_some() {
close_and_return_err!("The connection is already established" self);
close_and_return_err!("The connection is already established", self);
}
// Try to connect
self.stream = Some(try!(Connecter::connect(self.server_addr)));
// Log the connection
info!("connection established to {}",
self.stream.as_mut().unwrap().peer_name().unwrap());
//info!("connection established to {}",
// self.stream.as_mut().unwrap().peer_name().unwrap());
let result = try!(self.stream.as_mut().unwrap().get_reply());
@@ -255,7 +256,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
fn command(&mut self, command: Command) -> SmtpResult {
// for now we do not support SMTPUTF8
if !command.is_ascii() {
close_and_return_err!("Non-ASCII string" self);
close_and_return_err!("Non-ASCII string", self);
}
self.send_server(command, None)
@@ -279,7 +280,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
message, MESSAGE_ENDING
),
None => self.stream.as_mut().unwrap().send_and_get_response(
command.to_string().as_slice(), CRLF
format! ("{}", command) .as_slice(), CRLF
),
});
@@ -334,7 +335,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
None => close_and_return_err!(Response{
code: 503,
message: Some("Bad sequence of commands".to_string()),
} self),
}, self),
};
// Checks message encoding according to the server's capability
@@ -350,7 +351,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
if result.is_ok() {
// Log the mail command
info!("{}: from=<{}>", self.current_message.as_ref().unwrap(), from_address);
//info!("{}: from=<{}>", self.current_message.as_ref().unwrap(), from_address);
}
result
@@ -364,7 +365,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
if result.is_ok() {
// Log the rcpt command
info!("{}: to=<{}>", self.current_message.as_ref().unwrap(), to_address);
//info!("{}: to=<{}>", self.current_message.as_ref().unwrap(), to_address);
}
result
@@ -383,13 +384,13 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
None => close_and_return_err!(Response{
code: 503,
message: Some("Bad sequence of commands".to_string()),
} self)
}, self)
};
// Check message encoding
if !server_info.supports_feature(Extension::EightBitMime).is_some() {
if !message_content.is_ascii() {
close_and_return_err!("Server does not accepts UTF-8 strings" self);
if !message_content.as_bytes().is_ascii() {
close_and_return_err!("Server does not accepts UTF-8 strings", self);
}
}
@@ -399,7 +400,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
close_and_return_err!(Response{
code: 552,
message: Some("Message exceeds fixed maximum message size".to_string()),
} self);
}, self);
}
}
@@ -409,8 +410,8 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
// Increment the connection reuse counter
self.connection_reuse_count = self.connection_reuse_count + 1;
// Log the message
info!("{}: conn_use={}, size={}, status=sent ({})", self.current_message.as_ref().unwrap(),
self.connection_reuse_count, message_content.len(), result.as_ref().ok().unwrap());
//info!("{}: conn_use={}, size={}, status=sent ({})", self.current_message.as_ref().unwrap(),
// self.connection_reuse_count, message_content.len(), result.as_ref().ok().unwrap());
}
self.current_message = None;

View File

@@ -10,12 +10,12 @@
//! Information about a server
use std::fmt;
use std::fmt::{Show, Formatter};
use std::fmt::{Display, Formatter};
use extension::Extension;
/// Contains information about an SMTP server
#[deriving(Clone)]
#[derive(Clone,Debug)]
pub struct ServerInfo {
/// Server name
///
@@ -28,16 +28,14 @@ pub struct ServerInfo {
pub esmtp_features: Option<Vec<Extension>>,
}
impl Show for ServerInfo {
impl Display for ServerInfo {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write(
format!("{} with {}",
write!(f, "{} with {}",
self.name,
match self.esmtp_features {
Some(ref features) => features.to_string(),
Some(ref features) => format! ("{:?}", features),
None => "no supported features".to_string(),
}
).as_bytes()
)
}
}

View File

@@ -9,17 +9,17 @@
//! TODO
use std::io::net::tcp::TcpStream;
use std::io::IoResult;
use std::old_io::net::tcp::TcpStream;
use std::old_io::IoResult;
use std::str::from_utf8;
use std::vec::Vec;
use std::error::FromError;
use error::SmtpResult;
use response::Response;
use tools::{escape_crlf, escape_dot};
use tools::{escape_dot};
static BUFFER_SIZE: uint = 1024;
static BUFFER_SIZE: usize = 1024;
/// TODO
pub trait ClientStream {
@@ -36,7 +36,7 @@ impl ClientStream for TcpStream {
fn send_and_get_response(&mut self, string: &str, end: &str) -> SmtpResult {
try!(self.write_str(format!("{}{}", escape_dot(string), end).as_slice()));
debug!("Wrote: {}", escape_crlf(escape_dot(string).as_slice()));
//debug!("Wrote: {}", escape_crlf(escape_dot(string).as_slice()));
self.get_reply()
}
@@ -54,7 +54,7 @@ impl ClientStream for TcpStream {
Ok(bytes_read) => {
more = bytes_read == BUFFER_SIZE;
if bytes_read > 0 {
from_utf8(buf.slice_to(bytes_read)).unwrap()
from_utf8(&buf[..bytes_read]).unwrap()
} else {
""
}
@@ -64,7 +64,7 @@ impl ClientStream for TcpStream {
};
result.push_str(response);
}
debug!("Read: {}", escape_crlf(result.as_slice()));
//debug!("Read: {}", escape_crlf(result.as_slice()));
return Ok(result);
}
@@ -73,8 +73,8 @@ impl ClientStream for TcpStream {
let response = try!(self.read_into_string());
match response.as_slice().parse::<Response>() {
Some(response) => Ok(response),
None => Err(FromError::from_error("Could not parse response"))
Ok(response) => Ok(response),
Err(_) => Err(FromError::from_error("Could not parse response"))
}
}
}

View File

@@ -11,8 +11,9 @@
#![unstable]
use std::ascii::AsciiExt;
use std::error::FromError;
use std::fmt::{Show, Formatter, Result};
use std::fmt::{Display, Formatter, Result};
use response::Response;
use error::SmtpResult;
@@ -22,7 +23,7 @@ use common::SP;
///
/// We do not implement the following SMTP commands, as they were deprecated in RFC 5321
/// and must not be used by clients: `SEND`, `SOML`, `SAML`, `TURN`.
#[deriving(PartialEq,Eq,Clone)]
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum Command {
/// A fake command to represent the connection step
Connect,
@@ -54,9 +55,9 @@ pub enum Command {
Quit,
}
impl Show for Command {
impl Display for Command {
fn fmt(&self, f: &mut Formatter) -> Result {
f.write( match *self {
write! (f, "{}", match *self {
Command::Connect => "CONNECT".to_string(),
Command::StartTls => "STARTTLS".to_string(),
Command::ExtendedHello(ref my_hostname) => format!("EHLO {}", my_hostname),
@@ -76,7 +77,7 @@ impl Show for Command {
Command::Help(Some(ref argument)) => format!("HELP {}", argument),
Command::Noop => "NOOP".to_string(),
Command::Quit => "QUIT".to_string(),
}.as_bytes())
})
}
}

View File

@@ -11,7 +11,7 @@
#![unstable]
use std::io::net::ip::Port;
use std::old_io::net::ip::Port;
/// Default smtp port
pub static SMTP_PORT: Port = 25;

View File

@@ -12,14 +12,16 @@
#![unstable]
use std::error::Error;
use std::io::IoError;
use std::old_io::IoError;
use std::error::FromError;
use std::fmt::{Display, Formatter};
use std::fmt::Error as FmtError;
use response::Response;
use self::ErrorKind::{TransientError, PermanentError, UnknownError, InternalIoError};
/// An enum of all error kinds.
#[deriving(PartialEq, Eq, Clone, Show)]
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum ErrorKind {
/// Transient error
///
@@ -36,7 +38,7 @@ pub enum ErrorKind {
}
/// smtp error type
#[deriving(PartialEq, Eq, Clone, Show)]
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct SmtpError {
/// Error kind
pub kind: ErrorKind,
@@ -71,7 +73,7 @@ impl FromError<Response> for SmtpError {
let kind = match response.code/100 {
4 => TransientError(response),
5 => PermanentError(response),
_ => UnknownError(response.to_string()),
_ => UnknownError(format! ("{:?}", response)),
};
let desc = match kind {
TransientError(_) => "a permanent error occured during the SMTP transaction",
@@ -97,6 +99,17 @@ impl FromError<&'static str> for SmtpError {
}
}
impl Display for SmtpError {
fn fmt (&self, fmt: &mut Formatter) -> Result<(), FmtError> {
match self.kind {
TransientError(ref response) => write! (fmt, "{:?}", response),
PermanentError(ref response) => write! (fmt, "{:?}", response),
UnknownError(ref string) => write! (fmt, "{}", string),
InternalIoError(ref err) => write! (fmt, "{:?}", err.detail),
}
}
}
impl Error for SmtpError {
fn description(&self) -> &str {
match self.kind {
@@ -105,15 +118,6 @@ impl Error for SmtpError {
}
}
fn detail(&self) -> Option<String> {
match self.kind {
TransientError(ref response) => Some(response.to_string()),
PermanentError(ref response) => Some(response.to_string()),
UnknownError(ref string) => Some(string.to_string()),
InternalIoError(ref err) => err.detail.clone(),
}
}
fn cause(&self) -> Option<&Error> {
match self.kind {
InternalIoError(ref err) => Some(&*err as &Error),

View File

@@ -12,14 +12,15 @@
#![unstable]
use std::str::FromStr;
use std::fmt::{Show, Formatter, Result};
use std::fmt::{Display, Formatter, Result};
use std::result::Result as RResult;
use common::CRLF;
use response::Response;
use self::Extension::{EightBitMime, SmtpUtfEight, StartTls, Size};
/// Supported ESMTP keywords
#[deriving(PartialEq,Eq,Copy,Clone)]
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
pub enum Extension {
/// 8BITMIME keyword
///
@@ -36,38 +37,39 @@ pub enum Extension {
/// SIZE keyword
///
/// RFC 1427 : https://tools.ietf.org/html/rfc1427
Size(uint),
Size(usize),
}
impl Show for Extension {
impl Display for Extension {
fn fmt(&self, f: &mut Formatter) -> Result {
f.write(
write! (f, "{}",
match self {
&EightBitMime => "8BITMIME".to_string(),
&SmtpUtfEight => "SMTPUTF8".to_string(),
&StartTls => "STARTTLS".to_string(),
&Size(ref size) => format!("SIZE={}", size)
}.as_bytes()
}
)
}
}
impl FromStr for Extension {
// TODO: check RFC
fn from_str(s: &str) -> Option<Extension> {
type Err = &'static str;
fn from_str(s: &str) -> RResult<Extension, &'static str> {
let splitted : Vec<&str> = s.splitn(1, ' ').collect();
match splitted.len() {
1 => match splitted[0] {
"8BITMIME" => Some(EightBitMime),
"SMTPUTF8" => Some(SmtpUtfEight),
"STARTTLS" => Some(StartTls),
_ => None,
"8BITMIME" => Ok(EightBitMime),
"SMTPUTF8" => Ok(SmtpUtfEight),
"STARTTLS" => Ok(StartTls),
_ => Err("Unknown extension"),
},
2 => match (splitted[0], splitted[1].parse::<uint>()) {
("SIZE", Some(size)) => Some(Size(size)),
_ => None,
2 => match (splitted[0], splitted[1].parse::<usize>()) {
("SIZE", Ok(size)) => Ok(Size(size)),
_ => Err("Can't parse size"),
},
_ => None,
_ => Err("Empty extension?"),
}
}
}
@@ -87,9 +89,9 @@ impl Extension {
/// Parses supported ESMTP features
pub fn parse_esmtp_response(message: &str) -> Option<Vec<Extension>> {
let mut esmtp_features = Vec::new();
for line in message.split_str(CRLF) {
if let Some(Response{code: 250, message}) = line.parse::<Response>() {
if let Some(keyword) = message.unwrap().as_slice().parse::<Extension>() {
for line in message.split(CRLF) {
if let Ok(Response{code: 250, message}) = line.parse::<Response>() {
if let Ok(keyword) = message.unwrap().as_slice().parse::<Extension>() {
esmtp_features.push(keyword);
};
}

View File

@@ -127,12 +127,12 @@
#![crate_type = "lib"]
#![doc(html_root_url = "http://amousset.github.io/rust-smtp/smtp/")]
#![experimental]
#![feature(phase, macro_rules, default_type_params)]
#![deny(missing_docs, warnings)]
#![deny(missing_docs)]
#![feature(phase)] #[phase(plugin, link)] extern crate log;
#![feature(plugin,core,old_io,io)]
#![plugin(log)]
extern crate log;
extern crate time;
extern crate uuid;

View File

@@ -9,7 +9,7 @@
//! Simple SMTP "address" (very incomplete)
use std::fmt::{Show, Formatter, Result};
use std::fmt::{Display, Formatter, Result};
use common::SP;
@@ -39,7 +39,7 @@ impl<'a> ToAddress for (&'a str, &'a str) {
}
/// Contains an address with an optionnal alias
#[deriving(PartialEq,Eq,Clone)]
#[derive(PartialEq,Eq,Clone,Debug)]
pub struct Address {
/// The address
address: String,
@@ -47,12 +47,12 @@ pub struct Address {
alias: Option<String>,
}
impl Show for Address {
impl Display for Address {
fn fmt(&self, f: &mut Formatter) -> Result {
f.write(match self.alias {
write! (f, "{}", match self.alias {
Some(ref alias_string) => format!("{}{}<{}>", alias_string, SP, self.address.as_slice()),
None => self.address.clone(),
}.as_bytes())
})
}
}

View File

@@ -11,7 +11,7 @@
use time::Tm;
use std::fmt::{Show, Formatter, Result};
use std::fmt::{Display, Formatter, Result};
use common::{SP, COLON};
use mailer::address::Address;
@@ -36,7 +36,7 @@ impl<'a> ToHeader for (&'a str, &'a str) {
}
/// Contains a header
#[deriving(PartialEq,Eq,Clone)]
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum Header {
/// `To`
To(Address),
@@ -62,9 +62,9 @@ pub enum Header {
Other(String, String),
}
impl Show for Header {
impl Display for Header {
fn fmt(&self, f: &mut Formatter) -> Result {
f.write(format!("{}{}{}{}",
write! (f, "{}{}{}{}",
match *self {
Header::To(_) => "To",
Header::From(_) => "From",
@@ -80,18 +80,18 @@ impl Show for Header {
},
COLON, SP,
match *self {
Header::To(ref address) => address.to_string(),
Header::From(ref address) => address.to_string(),
Header::Cc(ref address) => address.to_string(),
Header::ReplyTo(ref address) => address.to_string(),
Header::Sender(ref address) => address.to_string(),
Header::To(ref address) => format! ("{}", address),
Header::From(ref address) => format! ("{}", address),
Header::Cc(ref address) => format! ("{}", address),
Header::ReplyTo(ref address) => format! ("{}", address),
Header::Sender(ref address) => format! ("{}", address),
Header::Date(ref date) => Tm::rfc822(date).to_string(),
Header::Subject(ref subject) => subject.clone(),
Header::MimeVersion => "1.0".to_string(),
Header::ContentType(ref string) => string.clone(),
Header::MessageId(ref string) => string.clone(),
Header::Other(_, ref value) => value.clone(),
}).as_bytes())
})
}
}

View File

@@ -9,7 +9,7 @@
//! Simple email (very incomplete)
use std::fmt::{Show, Formatter, Result};
use std::fmt::{Display, Formatter, Result};
use time::{now, Tm};
@@ -22,7 +22,7 @@ pub mod header;
pub mod address;
/// Simple email representation
#[deriving(PartialEq,Eq,Clone)]
#[derive(PartialEq,Eq,Clone,Debug)]
pub struct Email {
/// Array of headers
headers: Vec<Header>,
@@ -34,14 +34,14 @@ pub struct Email {
from: Option<String>,
}
impl Show for Email {
impl Display for Email {
fn fmt(&self, f: &mut Formatter) -> Result {
let mut formatted_headers = String::new();
for header in self.headers.iter() {
formatted_headers.push_str(header.to_string().as_slice());
formatted_headers.push_str(format! ("{}", header) .as_slice());
formatted_headers.push_str(CRLF);
}
f.write(format!("{}{}{}", formatted_headers, CRLF, self.body).as_bytes())
write! (f, "{}{}{}", formatted_headers, CRLF, self.body)
}
}
@@ -147,7 +147,7 @@ impl SendableEmail for Email {
}
fn message(&self) -> String {
self.to_string()
format! ("{}", self)
}
/// Adds a `Message-ID` header

View File

@@ -12,14 +12,15 @@
#![unstable]
use std::str::FromStr;
use std::fmt::{Show, Formatter, Result};
use std::fmt::{Display, Formatter, Result};
use std::result::Result as RResult;
use tools::remove_trailing_crlf;
/// Contains an SMTP reply, with separed code and message
///
/// The text message is optional, only the code is mandatory
#[deriving(PartialEq,Eq,Clone)]
#[derive(PartialEq,Eq,Clone,Debug)]
pub struct Response {
/// Server response code
pub code: u16,
@@ -27,43 +28,44 @@ pub struct Response {
pub message: Option<String>
}
impl Show for Response {
impl Display for Response {
fn fmt(&self, f: &mut Formatter) -> Result {
f.write(
write! (f, "{}",
match self.clone().message {
Some(message) => format!("{} {}", self.code, message),
None => format!("{}", self.code),
}.as_bytes()
}
)
}
}
impl FromStr for Response {
fn from_str(s: &str) -> Option<Response> {
type Err = &'static str;
fn from_str(s: &str) -> RResult<Response, &'static str> {
// If the string is too short to be a response code
if s.len() < 3 {
None
Err("len < 3")
// If we have only a code, with or without a trailing space
} else if s.len() == 3 || (s.len() == 4 && s.slice(3,4) == " ") {
match s.slice_to(3).parse::<u16>() {
Some(code) => Some(Response{
} else if s.len() == 3 || (s.len() == 4 && &s[3..4] == " ") {
match s[..3].parse::<u16>() {
Ok(code) => Ok(Response{
code: code,
message: None
}),
None => None,
Err(_) => Err("Can't parse the code"),
}
// If we have a code and a message
} else {
match (
s.slice_to(3).parse::<u16>(),
vec![" ", "-"].contains(&s.slice(3,4)),
(remove_trailing_crlf(s.slice_from(4)))
s[..3].parse::<u16>(),
vec![" ", "-"].contains(&&s[3..4]),
(remove_trailing_crlf(&s[4..]))
) {
(Some(code), true, message) => Some(Response{
(Ok(code), true, message) => Ok(Response{
code: code,
message: Some(message.to_string())
}),
_ => None,
_ => Err("Error parsing a code with a message"),
}
}
}

View File

@@ -17,9 +17,9 @@ use common::{CR, LF, CRLF};
#[inline]
pub fn remove_trailing_crlf(string: &str) -> &str {
if string.ends_with(CRLF) {
string.slice_to(string.len() - 2)
&string[.. string.len() - 2]
} else if string.ends_with(CR) {
string.slice_to(string.len() - 1)
&string[.. string.len() - 1]
} else {
string
}
@@ -28,7 +28,7 @@ pub fn remove_trailing_crlf(string: &str) -> &str {
/// Returns the first word of a string, or the string if it contains no space
#[inline]
pub fn get_first_word(string: &str) -> &str {
string.split_str(CRLF).next().unwrap().splitn(1, ' ').next().unwrap()
string.split(CRLF).next().unwrap().splitn(1, ' ').next().unwrap()
}
/// Returns the string replacing all the CRLF with "\<CRLF\>"

View File

@@ -12,13 +12,13 @@
#![unstable]
use std::fmt;
use std::fmt::{Show, Formatter};
use std::fmt::{Display, Formatter};
use command::Command;
use self::TransactionState::{Unconnected, Connected, HelloSent, MailSent, RecipientSent, DataSent};
/// Contains the state of the current transaction
#[deriving(PartialEq,Eq,Copy)]
#[derive(PartialEq,Eq,Copy,Debug)]
pub enum TransactionState {
/// No connection was established
Unconnected,
@@ -34,9 +34,9 @@ pub enum TransactionState {
DataSent,
}
impl Show for TransactionState {
impl Display for TransactionState {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write(
write! (f, "{}",
match *self {
Unconnected => "Unconnected",
Connected => "Connected",
@@ -44,7 +44,7 @@ impl Show for TransactionState {
MailSent => "MailSent",
RecipientSent => "RecipientSent",
DataSent => "DataSent",
}.as_bytes()
}
)
}
}