Changing the current directory

Let's add another case in our match expression in the handle_cmd() method:

#[async]
fn handle_cmd(mut self, cmd: Command) -> Result<Self> {
    match cmd {
        Command::Cwd(directory) => self = await!(self.cwd(directory))?,
        // …
    }
}

It simply calls the following method:

#[async]
fn cwd(mut self, directory: PathBuf) -> Result<Self> {
    let path = self.cwd.join(&directory);
    let (new_self, res) = self.complete_path(path);
    self = new_self;
    if let Ok(dir) = res {
        let (new_self, res) = self.strip_prefix(dir);
        self = new_self;
        if let Ok(prefix) = res {
            self.cwd = prefix.to_path_buf();
            self = await!(self.send(Answer::new(ResultCode::Ok,
                                                &format!("Directory changed to \" 
{}\"", directory.display()))))?; return Ok(self) } } self = await!(self.send(Answer::new(ResultCode::FileNotFound, "No such file or directory")))?; Ok(self) }

This code uses the following two methods, which are similar to those in the previous chapter:

use std::path::StripPrefixError;

fn complete_path(self, path: PathBuf) -> (Self, result::Result<PathBuf, io::Error>) {
    let directory = self.server_root.join(if path.has_root() {
        path.iter().skip(1).collect()
    } else {
        path
    });
    let dir = directory.canonicalize();
    if let Ok(ref dir) = dir {
        if !dir.starts_with(&self.server_root) {
            return (self, 
Err(io::ErrorKind::PermissionDenied.into())); } } (self, dir) } fn strip_prefix(self, dir: PathBuf) -> (Self, result::Result<PathBuf, StripPrefixError>) { let res = dir.strip_prefix(&self.server_root).map(|p| p.to_path_buf()); (self, res) }

Since it uses a new attribute, let's add it to the Client structure:

struct Client {
    cwd: PathBuf,
    server_root: PathBuf,
    writer: Writer,
}

We also add its constructor:

impl Client {
    fn new(writer: Writer, server_root: PathBuf) -> Client {
        Client {
            cwd: PathBuf::from("/"),
            server_root,
            writer,
        }
    }
}

We also need to pass this value in a few places, first, in the client function and its wrapper:

#[async]
fn client(stream: TcpStream, server_root: PathBuf) -> Result<()> {
    // …
    let mut client = Client::new(writer, server_root);
    // …
}

#[async]
fn handle_client(stream: TcpStream, server_root: PathBuf) -> result::Result<(), ()> {
    await!(client(stream, server_root))
        .map_err(|error| println!("Error handling client: {}", 
error)) }

Then, we need to update the server function:

#[async]
fn server(handle: Handle, server_root: PathBuf) -> io::Result<()> {
    // …
    #[async]
    for (stream, addr) in listener.incoming() {
        let address = format!("[address : {}]", addr);
        println!("New client: {}", address);
        handle.spawn(handle_client(stream, server_root.clone()));
        println!("Waiting another client...");
    }
    Ok(())
}

To send the server root to the handle_client function call.

And finally, we'll update the main function to send it to the server function:

use std::env;

fn main() {
    let mut core = Core::new().expect("Cannot create tokio Core");
    let handle = core.handle();

    match env::current_dir() {
        Ok(server_root) => {
            if let Err(error) = core.run(server(handle, 
server_root)) { println!("Error running the server: {}", error); } } Err(e) => println!("Couldn't start server: {:?}", e), } }

Here, we send the current directory as the server root.