#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "event", rename_all = "snake_case"))]
#[non_exhaustive]
pub enum FtpMessage {
Command { verb: FtpCommand, args: String },
Reply { code: u16, text: String },
Banner { banner: String },
Credentials { user: String, pass: String },
TlsUpgrade,
Transfer {
kind: TransferKind,
filename: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum FtpCommand {
User,
Pass,
Acct,
Auth,
Cwd,
Cdup,
Pwd,
List,
Nlst,
Retr,
Stor,
Appe,
Stou,
Mkd,
Rmd,
Dele,
Rnfr,
Rnto,
Pasv,
Port,
Epsv,
Eprt,
Type,
Mode,
Stru,
Quit,
Noop,
Syst,
Stat,
Help,
Feat,
Opts,
Other(String),
}
impl FtpCommand {
pub fn from_verb(raw: &str) -> Self {
let upper = raw.to_ascii_uppercase();
match upper.as_str() {
"USER" => Self::User,
"PASS" => Self::Pass,
"ACCT" => Self::Acct,
"AUTH" => Self::Auth,
"CWD" => Self::Cwd,
"CDUP" => Self::Cdup,
"PWD" => Self::Pwd,
"LIST" => Self::List,
"NLST" => Self::Nlst,
"RETR" => Self::Retr,
"STOR" => Self::Stor,
"APPE" => Self::Appe,
"STOU" => Self::Stou,
"MKD" => Self::Mkd,
"RMD" => Self::Rmd,
"DELE" => Self::Dele,
"RNFR" => Self::Rnfr,
"RNTO" => Self::Rnto,
"PASV" => Self::Pasv,
"PORT" => Self::Port,
"EPSV" => Self::Epsv,
"EPRT" => Self::Eprt,
"TYPE" => Self::Type,
"MODE" => Self::Mode,
"STRU" => Self::Stru,
"QUIT" => Self::Quit,
"NOOP" => Self::Noop,
"SYST" => Self::Syst,
"STAT" => Self::Stat,
"HELP" => Self::Help,
"FEAT" => Self::Feat,
"OPTS" => Self::Opts,
_ => Self::Other(upper),
}
}
pub fn is_transfer(&self) -> bool {
matches!(self, Self::Retr | Self::Stor | Self::Appe | Self::Stou)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum FtpReplyClass {
PositivePreliminary,
PositiveCompletion,
PositiveIntermediate,
TransientNegative,
PermanentNegative,
Other,
}
impl FtpReplyClass {
pub fn classify(code: u16) -> Self {
match code / 100 {
1 => Self::PositivePreliminary,
2 => Self::PositiveCompletion,
3 => Self::PositiveIntermediate,
4 => Self::TransientNegative,
5 => Self::PermanentNegative,
_ => Self::Other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum TransferKind {
Download,
Upload,
Append,
UploadUnique,
}
impl TransferKind {
pub(crate) fn from_command(cmd: &FtpCommand) -> Option<Self> {
Some(match cmd {
FtpCommand::Retr => Self::Download,
FtpCommand::Stor => Self::Upload,
FtpCommand::Appe => Self::Append,
FtpCommand::Stou => Self::UploadUnique,
_ => return None,
})
}
}