rrun-ssh 0.3.0

Remote run utility; runs a command via SSH if the current directory was mounted via SSHFS.
Documentation
use std::collections::hash_map::*;
use std::env::current_dir;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::io;
use super::{Location, RemoteLocation};

/// Encapsulates data on which directories have been remotely mounted.
#[derive(Debug)]
pub struct RemoteMounts {
    map: HashMap<PathBuf, RemoteLocation>,
}
impl RemoteMounts {
    /// Loads the contents of an mtab file into a `RemoteMounts` structure.
    pub fn load_reader<R: Read>(r: R) -> io::Result<RemoteMounts> {
        let mut line = String::new();
        let mut reader = BufReader::new(r);

        let mut map = HashMap::new();
        while try!(reader.read_line(&mut line)) != 0 {
            {
                let mut split = line.split_whitespace();
                let remote = RemoteLocation::new(split.next().unwrap());
                if let Ok(remote) = remote {
                    let mount = Path::new(split.next().unwrap()).to_path_buf();
                    map.insert(mount, remote);
                }
            }
            line.clear();
        }
        Ok(RemoteMounts { map: map })
    }

    /// Loads the contents of `/etc/mtab` into a `RemoteMounts` structure.
    pub fn load() -> io::Result<RemoteMounts> {
        Self::load_reader(try!(File::open("/etc/mtab")))
    }

    /// Determines the location of the given directory.
    pub fn into_dir(mut self, p: PathBuf) -> Location {
        let curr_dir = p;
        let mut check = curr_dir.clone();
        loop {
            match self.map.entry(check.clone()) {
                Entry::Vacant(_) => {
                    if check.parent().is_none() {
                        return Location::Local;
                    }
                    check.pop();
                    continue;
                }
                Entry::Occupied(entry) => {
                    let mut remote = entry.remove();
                    remote.path.push(curr_dir.strip_prefix(&check).unwrap());
                    return Location::Remote(remote);
                }
            }
        }
    }

    /// Determines the location of the current directory.
    pub fn into_current_dir(self) -> io::Result<Location> {
        Ok(self.into_dir(try!(current_dir())))
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use super::RemoteMounts;
    use super::super::{Location, RemoteLocation};

    #[test]
    fn empty_mtab() {
        let mtab: &[u8] = &[];
        let dir = Path::new("/").to_path_buf();
        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir),
                   Location::Local);
    }

    #[test]
    fn local_mtab() {
        let mtab: &[u8] = b"\
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
dev /dev devtmpfs rw,nosuid,relatime,size=4046352k,nr_inodes=1011588,mode=755 0 0
run /run tmpfs rw,nosuid,nodev,relatime,mode=755 0 0
/dev/sda1 / btrfs rw,relatime,compress=lzo,space_cache 0 0
tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0
tmpfs /tmp tmpfs rw 0 0
";
        let dir = Path::new("/").to_path_buf();
        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir),
                   Location::Local);
    }

    #[test]
    fn remote_mtab() {
        let mtab: &[u8] = b"\
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
dev /dev devtmpfs rw,nosuid,relatime,size=4046352k,nr_inodes=1011588,mode=755 0 0
example:/ /media/example fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
run /run tmpfs rw,nosuid,nodev,relatime,mode=755 0 0
/dev/sda1 / btrfs rw,relatime,compress=lzo,space_cache 0 0
example.com:/home/user /run/user/1000/media/example fuse.sshfs rw,nosuid,nodev,relatime,\
user_id=1000,group_id=100 0 0
tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
user@example.com:/home/user /mnt fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
example.com:/home /mnt fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0
tmpfs /tmp tmpfs rw 0 0
";
        let dir1 = Path::new("/").to_path_buf();
        let dir2 = Path::new("/media/example").to_path_buf();
        let dir3 = Path::new("/run/media").to_path_buf();
        let dir4 = Path::new("/mnt/user").to_path_buf();
        let remote2 = RemoteLocation::new("example:/").unwrap();
        let remote4 = RemoteLocation::new("example.com:/home/user").unwrap();
        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir1),
                   Location::Local);
        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir2),
                   Location::Remote(remote2));
        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir3),
                   Location::Local);
        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir4),
                   Location::Remote(remote4));
    }
}