use crate::server::{Command, FTPError, InternalMsg, Reply};
use bytes::BytesMut;
use std::io::Write;
use tokio_codec::{Decoder, Encoder};
#[derive(Debug)]
pub enum Event {
Command(Command),
InternalMsg(InternalMsg),
}
pub struct FTPCodec {
next_index: usize,
}
impl FTPCodec {
pub fn new() -> Self {
FTPCodec { next_index: 0 }
}
}
impl Decoder for FTPCodec {
type Item = Command;
type Error = FTPError;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Command>, Self::Error> {
if let Some(newline_offset) = buf[self.next_index..].iter().position(|b| *b == b'\n') {
let newline_index = newline_offset + self.next_index;
let line = buf.split_to(newline_index + 1);
self.next_index = 0;
Ok(Some(Command::parse(line)?))
} else {
self.next_index = buf.len();
Ok(None)
}
}
}
impl Encoder for FTPCodec {
type Item = Reply;
type Error = FTPError;
fn encode(&mut self, reply: Reply, buf: &mut BytesMut) -> Result<(), Self::Error> {
let mut buffer = vec![];
match reply {
Reply::None => {
return Ok(());
}
Reply::CodeAndMsg { code, msg } => {
if msg.is_empty() {
writeln!(buffer, "{}\r", code as u32)?;
} else {
writeln!(buffer, "{} {}\r", code as u32, msg)?;
}
}
Reply::MultiLine { code, mut lines } => {
let last_line = lines.pop().unwrap();
for it in lines.iter_mut() {
if it.chars().nth(0).unwrap().is_digit(10) {
it.insert(0, ' ');
}
}
if lines.is_empty() {
writeln!(buffer, "{} {}\r", code as u32, last_line)?;
} else {
write!(buffer, "{}-{}\r\n{} {}\r\n", code as u32, lines.join("\r\n"), code as u32, last_line)?;
}
}
}
buf.extend(&buffer);
Ok(())
}
}