mtab 0.1.0

`/etc/mtab` parser
Documentation
use anyhow::{bail, Result};
use std::fs;
use std::path::{Path, PathBuf};
use std::rc::Rc;

#[derive(Debug, PartialEq, Eq)]
pub struct Mount {
    pub device: String,
    pub mountpoint: PathBuf,
    pub fs: String,
    pub options: Vec<String>,
    pub dump_level: u8,
    pub fsck_passno: u8,
}

pub fn parse_mtab_line(string: &str) -> Result<Mount> {
    let mut fields = string.split_ascii_whitespace();
    macro_rules! next {
        (expecting $lit:literal) => {
            if let Some(field) = fields.next() {
                field
            } else {
                bail!("expected {}\n, but got EOL", $lit);
            }
        };
    }

    let device = next!(expecting "a device").to_string();
    let mountpoint = PathBuf::from(next!(expecting "a mountpoint"));
    let fs = next!(expecting "a filesystem type").to_string();
    let options = next!(expecting "a list of options")
        .split(',')
        .map(str::to_string)
        .collect();
    let dump_level = next!(expecting "an integer").parse()?;
    let fsck_passno = next!(expecting "an integer").parse()?;

    let mount = Mount {
        device,
        mountpoint,
        fs,
        options,
        dump_level,
        fsck_passno,
    };

    Ok(mount)
}

pub fn read_mtab_from<P: AsRef<Path>>(path: P) -> Result<Rc<[Mount]>> {
    let lines: Result<Rc<[_]>> = fs::read_to_string(path.as_ref())?
        .lines()
        .map(parse_mtab_line)
        .collect();
    let lines = lines?;
    Ok(lines)
}

pub fn read_mtab() -> Result<Rc<[Mount]>> {
    read_mtab_from("/etc/mtab")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let line = "/dev/sda1 /mnt/indarys ext4 rw,nosuid,nodev 1 2";
        let parsed_mount = parse_mtab_line(line).expect("should be parseable");
        let manual_mount = Mount {
            device: "/dev/sda1".to_string(),
            mountpoint: PathBuf::from("/mnt/indarys"),
            fs: "ext4".to_string(),
            options: vec!["rw".to_string(), "nosuid".to_string(), "nodev".to_string()],
            dump_level: 1,
            fsck_passno: 2,
        };
        assert_eq!(parsed_mount, manual_mount);
    }

    #[cfg(unix)]
    #[test]
    fn it_really_works() {
        assert!(read_mtab().is_ok())
    }
}