Now that we know how to play with paths nicely, it'd be a shame not fully implement the LIST command, right?
To complete it, you'll need to update the Command::List variant in order to make it accept PathBuf as an argument.
So, we currently have the following code:
Command::List => { if let Some(ref mut data_writer) = self.data_writer { let mut tmp = PathBuf::from("."); send_cmd(&mut self.stream, ResultCode::DataConnectionAlreadyOpen, "Starting to list directory..."); let mut out = String::new(); for entry in read_dir(tmp).unwrap() { for entry in dir { if let Ok(entry) = entry { add_file_info(entry.path(), &mut out); } } send_data(data_writer, &out) } } else { send_cmd(&mut self.stream, ResultCode::ConnectionClosed, "No opened data
connection"); } if self.data_writer.is_some() { self.data_writer = None; send_cmd(&mut self.stream, ResultCode::ClosingDataConnection, "Transfer
done"); } }
Let's update it as follows:
Command::List(path) => { if let Some(ref mut data_writer) = self.data_writer { let server_root = env::current_dir().unwrap(); let path = self.cwd.join(path.unwrap_or_default()); let directory = PathBuf::from(&path); if let Ok(path) = self.complete_path(directory,
&server_root) { send_cmd(&mut self.stream,
ResultCode::DataConnectionAlreadyOpen,
"Starting to list directory..."); let mut out = String::new(); for entry in read_dir(path).unwrap() { for entry in dir { if let Ok(entry) = entry { add_file_info(entry.path(), &mut out); } } send_data(data_writer, &out) } } else { send_cmd(&mut self.stream, ResultCode::InvalidParameterOrArgument, "No such file or directory..."); } } else { send_cmd(&mut self.stream, ResultCode::ConnectionClosed,
"No opened data connection"); } if self.data_writer.is_some() { self.data_writer = None; send_cmd(&mut self.stream,
ResultCode::ClosingDataConnection, "Transfer done"); } }
To put it simply, we just added the following line:
let path = self.cwd.join(path.unwrap_or_default()); let directory = PathBuf::from(&path); if let Ok(path) = self.complete_path(directory, &server_root) { // ... } else { send_cmd(&mut self.stream, ResultCode::InvalidParameterOrArgument, "No such file or directory..."); }
Thanks to the Client::complete_path method, things were pretty easy with the path manipulation. So, what happens if the given path is a file? We don't check such a case but we should! Let's replace the following lines:
for entry in read_dir(path).unwrap() { for entry in dir { if let Ok(entry) = entry { add_file_info(entry.path(), &mut out); } } send_data(data_writer, &out) }
With:
if path.is_dir() { for entry in read_dir(path).unwrap() { for entry in dir { if let Ok(entry) = entry { add_file_info(entry.path(), &mut out); } } send_data(data_writer, &out) } } else { add_file_info(path, &mut out); }
And that's it! Luckily for us, we did things correctly the first time, so it just works.