plan9 0.1.1

Plan9 interaction, based on github.com/9fans/go
Documentation
use std::cmp;
use std::env;
use std::io;

use anyhow::Result;
use nine::p2000::{OpenMode, Qid};

use crate::conn::Conn;

pub fn get_user() -> String {
    env::var("USER").unwrap()
}

pub struct Fid {
    pub c: Conn,

    pub qid: Qid,
    pub fid: u32,
    pub mode: OpenMode,
    offset: u64,
}

impl Fid {
    pub fn new(c: Conn, fid: u32, qid: Qid) -> Fid {
        Fid {
            c: c.clone(),
            qid,
            fid,
            mode: OpenMode::READ,
            offset: 0,
        }
    }
    pub fn walk(&mut self, name: &str) -> Result<Fid> {
        let wfid = self.c.newfid();
        let mut fid = self.fid;

        let name = String::from(name);
        let mut elem: Vec<String> = name
            .split("/")
            .filter(|&x| x != "" && x != ".")
            .map(|x| x.to_string())
            .collect();
        let mut qid: Qid;

        const MAXWELEM: usize = 16;
        loop {
            let n = cmp::min(elem.len(), MAXWELEM);
            let wname = elem[0..n].to_vec();
            elem.drain(0..n);
            let qids = self.c.walk(fid, wfid, wname)?;
            qid = if n == 0 {
                self.qid.clone()
            } else {
                qids[n - 1].clone()
            };
            if elem.len() == 0 {
                break;
            }
            fid = wfid;
        }
        Ok(Fid::new(self.c.clone(), wfid, qid))
    }

    pub fn open(&mut self, mode: OpenMode) -> Result<()> {
        self.c.open(self.fid, mode)?;
        self.mode = mode;
        Ok(())
    }
}

const IOHDRSZ: u32 = 24;

impl io::Read for Fid {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let msize = self.c.msize - IOHDRSZ;
        let n: u32 = cmp::min(buf.len() as u32, msize);
        let data = match self.c.read(self.fid, self.offset, n) {
            Ok(r) => r,
            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{}", e))),
        };
        for (i, x) in data.iter().enumerate() {
            buf[i] = *x
        }
        self.offset += data.len() as u64;
        Ok(data.len())
    }
}

impl io::Seek for Fid {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        match pos {
            io::SeekFrom::Start(n) => self.offset = n,
            io::SeekFrom::Current(n) => {
                if n >= 0 {
                    self.offset += n as u64;
                } else {
                    self.offset -= n as u64;
                }
            }
            io::SeekFrom::End(_) => {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("seeking to end unsupported"),
                ))
            }
        }
        Ok(self.offset)
    }
}

impl io::Write for Fid {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let msize = (self.c.msize - IOHDRSZ) as usize;
        let mut tot: usize = 0;
        let n = buf.len();
        let mut first = true;
        while tot < n || first {
            let want: usize = cmp::min(n - tot, msize);
            let got = match self
                .c
                .write(self.fid, self.offset, buf[tot..tot + want].to_vec())
            {
                Ok(r) => r as usize,
                Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{}", e))),
            };
            tot += got;
            self.offset += got as u64;
            first = false;
        }
        Ok(tot)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl Drop for Fid {
    fn drop(&mut self) {
        let _ = self.c.clunk(self.fid);
    }
}