<commit_before>// Copyright 2017 Amos Onn.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

use std::fs::File;
use std::cmp::min;
use std::cell::RefCell;
use std::sync::{Mutex, RwLock};
use std::os::unix::fs::FileExt;
use std::ffi::OsStr;
use std::path::Path;
use libc;

use super::fusefl::*;
use fuse_mt::*;
use super::Result;

/// Trait to be implemented for providing the "reader" functionality, to be used with
/// FilesystemFLOpen or FilesystemFLRwOpen.
pub trait ReadFileLike {
    /// Read data.
    /// Read should send exactly the number of bytes requested except on EOF or error,
    /// otherwise the rest of the data will be substituted with zeroes. An exception to
    /// this is when the file has been opened in 'direct_io' mode, in which case the
    /// return value of the read system call will reflect the return value of this
    /// operation.
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize>;
}

/// Trait to be implemented for providing the "writer" functionality, to be used with
/// FilesystemFLOpen or FilesystemFLRwOpen.
pub trait WriteFileLike {
    /// Write data.
    /// Write should return exactly the number of bytes requested except on error. An
    /// exception to this is when the file has been opened in 'direct_io' mode, in
    /// which case the return value of the write system call will reflect the return
    /// value of this operation.
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize>;

    /// Synchronize file contents.
    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

//FIXME: both of the below impl-s don't guarantee filling the buffer!

impl ReadFileLike for File {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        FileExt::read_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }
}

impl WriteFileLike for File {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        FileExt::write_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }

    // NOTE: we can't use the flush method from Write, because that wants a &mut. However, for now
    // File's impl of flush is the same as ours, Ok(()), so we can stick with that.
    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

impl ReadFileLike for [u8] {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        if offset > usize::max_value() as u64 { return Ok(0); }
        let offset = offset as usize;
        let len = min(buf.len(), self.len() - offset);
        buf[..len].copy_from_slice(&self[offset..offset+len]);
        Ok(len)
    }
}

fn do_write_at(this: &mut [u8], buf: &[u8], offset: u64) -> usize {
    if offset > usize::max_value() as u64 { return 0; }
    let offset = offset as usize;
    let len = min(buf.len(), this.len() - offset);
    this[offset..offset+len].copy_from_slice(&buf[..len]);
    len
}

impl WriteFileLike for RefCell<[u8]> {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Ok(do_write_at(&mut *self.borrow_mut(), buf, offset))
    }
}

impl WriteFileLike for Mutex<[u8]> {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Ok(do_write_at(&mut *self.lock().unwrap(), buf, offset))
    }
}

impl WriteFileLike for RwLock<[u8]> {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Ok(do_write_at(&mut *self.write().unwrap(), buf, offset))
    }
}

/// Empty type for using with FilesystemFLRwOpen as the WriteLike and ReadWriteLike for readonly
/// fs-s (or the similar parallel for writeonly ones).
pub enum NoFile {}

#[allow(unused_variables)]
impl ReadFileLike for NoFile {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }
}

#[allow(unused_variables)]
impl WriteFileLike for NoFile {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }

    fn flush(&self) -> Result<()> {
        Err(libc::ENOSYS)
    }
}

/// Naive implementation of a read-write FileLike, given a read FileLike and
/// a write FileLike implementation.
pub struct ReadWriteAdaptor<R, W> {
    reader: R,
    writer: W,
}

impl<R, W> ReadFileLike for ReadWriteAdaptor<R, W> where R: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        self.reader.read_at(buf, offset)
    }
}

impl<R, W> WriteFileLike for ReadWriteAdaptor<R, W> where W: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        self.writer.write_at(buf, offset)
    }

    fn flush(&self) -> Result<()> {
        self.writer.flush()
    }
}

/// Implementation of a FileLike which can be either read-only, write-only or read-write.
/// Implementes both the ReadLike and WriteLike, returning EBADF in case of trying to write to a
/// read-only file or vice-versa (just like you'd expect).
pub enum ModalFileLike<R, W, RW> {
    ReadOnly(R),
    WriteOnly(W),
    ReadWrite(RW),
}

use self::ModalFileLike::*;

impl<R, W, RW> ReadFileLike for ModalFileLike<R, W, RW> where
    R: ReadFileLike, RW: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(ref r) => r.read_at(buf, offset),
            WriteOnly(_) => Err(libc::EBADF),
            ReadWrite(ref rw) => rw.read_at(buf, offset),
        }
    }
}

impl<R, W, RW> WriteFileLike for ModalFileLike<R, W, RW> where
    W: WriteFileLike, RW: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.write_at(buf, offset),
            ReadWrite(ref rw) => rw.write_at(buf, offset),
        }
    }

    fn flush(&self) -> Result<()> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.flush(),
            ReadWrite(ref rw) => rw.flush(),
        }
    }
}

/// Trait for using different types for the read, write and read-write files. The read-write type
/// can be a ReadWriteAdaptor over the read and write ones.
/// Everything implementing this implements FilesystemFLOpen, dispatching the open and create calls
/// according to the ACCMODE to the open or create method for the right type of file.
/// NOTE: fsync_metadata is not dispatched, but rather gets an enum over the possible file types,
/// because persumably the synchronization of metadata is mostly unaffected by how the file was
/// opened.
pub trait FilesystemFLRwOpen {
    type ReadLike: ReadFileLike; // = NoFile;
    type WriteLike: WriteFileLike; // = NoFile;
    type ReadWriteLike: ReadFileLike+WriteFileLike; // = ReadWriteAdaptor<Self::ReadLike, Self::WriteLike>;

    fn open_read(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn open_write(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn open_readwrite(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_read(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn create_write(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_readwrite(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &ModalFileLike<
        Self::ReadLike,
        Self::WriteLike,
        Self::ReadWriteLike,
    >) -> ResultEmpty {
        Err(libc::ENOSYS)
    }
}

/// Trait for standard usecase of FilesystemFL - open and create methods return a FileLike object,
/// which supports reading, writing and flushing of data, and then these calls are passed directly
/// to it. Once specialization (RFC #1210) lands, this will be integrated as a default impl of
/// FilesystemFL for types implementing this; for now, this repeats some methods from FilesystemFL,
/// and these should be manually called in the implementation of FilesystemFL.
// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
pub trait FilesystemFLOpen {
    type FileLike: ReadFileLike+WriteFileLike;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    fn read(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _size: u32) -> ResultData {
        let _size = _size as usize;
        let mut vec = Vec::<u8>::with_capacity(_size);
        unsafe { vec.set_len(_size) };
        let num_read = _fl.read_at(vec.as_mut_slice(), _offset)?;
        assert!(num_read <= _size);
        unsafe { vec.set_len(num_read) };
        Ok(vec)
    }

    fn write(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _data: Vec<u8>, _flags: u32) -> ResultWrite {
        assert!(_data.len() <= u32::max_value() as usize);
        _fl.write_at(_data.as_slice(), _offset).map(|x| x as u32)
    }

    fn fsync(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _datasync: bool) -> ResultEmpty {
        _fl.flush()?;
        if !_datasync {
            self.fsync_metadata(_req, _path, _fl)
        } else {
            Ok(())
        }
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike) -> ResultEmpty {
        Err(libc::ENOSYS)
    }
}

// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
impl<T> FilesystemFLOpen for T where T: FilesystemFLRwOpen {
    type FileLike = ModalFileLike<
        <Self as FilesystemFLRwOpen>::ReadLike,
        <Self as FilesystemFLRwOpen>::WriteLike,
        <Self as FilesystemFLRwOpen>::ReadWriteLike,
    >;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_open(self.open_read(_req, _path, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_open(self.open_write(_req, _path, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_open(self.open_readwrite(_req, _path, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_create(self.create_read(_req, _parent, _name, _mode, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_create(self.create_write(_req, _parent, _name, _mode, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_create(self.create_readwrite(_req, _parent, _name, _mode, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike) -> ResultEmpty {
        FilesystemFLRwOpen::fsync_metadata(self, _req, _path, _fl)
    }
}

<commit_msg>impl'd ReadFileLike for &[u8].<commit_after>// Copyright 2017 Amos Onn.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

use std::fs::File;
use std::cmp::min;
use std::cell::RefCell;
use std::sync::{Mutex, RwLock};
use std::os::unix::fs::FileExt;
use std::ffi::OsStr;
use std::path::Path;
use libc;

use super::fusefl::*;
use fuse_mt::*;
use super::Result;

/// Trait to be implemented for providing the "reader" functionality, to be used with
/// FilesystemFLOpen or FilesystemFLRwOpen.
pub trait ReadFileLike {
    /// Read data.
    /// Read should send exactly the number of bytes requested except on EOF or error,
    /// otherwise the rest of the data will be substituted with zeroes. An exception to
    /// this is when the file has been opened in 'direct_io' mode, in which case the
    /// return value of the read system call will reflect the return value of this
    /// operation.
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize>;
}

/// Trait to be implemented for providing the "writer" functionality, to be used with
/// FilesystemFLOpen or FilesystemFLRwOpen.
pub trait WriteFileLike {
    /// Write data.
    /// Write should return exactly the number of bytes requested except on error. An
    /// exception to this is when the file has been opened in 'direct_io' mode, in
    /// which case the return value of the write system call will reflect the return
    /// value of this operation.
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize>;

    /// Synchronize file contents.
    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

//FIXME: both of the below impl-s don't guarantee filling the buffer!

impl ReadFileLike for File {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        FileExt::read_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }
}

impl WriteFileLike for File {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        FileExt::write_at(self, buf, offset).map_err(|x| x.raw_os_error().unwrap())
    }

    // NOTE: we can't use the flush method from Write, because that wants a &mut. However, for now
    // File's impl of flush is the same as ours, Ok(()), so we can stick with that.
    fn flush(&self) -> Result<()> {
        Ok(())
    }
}

impl ReadFileLike for [u8] {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        (&self).read_at(buf, offset)
    }
}

impl<'a> ReadFileLike for &'a [u8] {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        if offset > usize::max_value() as u64 { return Ok(0); }
        let offset = offset as usize;
        let len = min(buf.len(), self.len() - offset);
        buf[..len].copy_from_slice(&self[offset..offset+len]);
        Ok(len)
    }
}

fn do_write_at(this: &mut [u8], buf: &[u8], offset: u64) -> usize {
    if offset > usize::max_value() as u64 { return 0; }
    let offset = offset as usize;
    let len = min(buf.len(), this.len() - offset);
    this[offset..offset+len].copy_from_slice(&buf[..len]);
    len
}

impl WriteFileLike for RefCell<[u8]> {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Ok(do_write_at(&mut *self.borrow_mut(), buf, offset))
    }
}

impl WriteFileLike for Mutex<[u8]> {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Ok(do_write_at(&mut *self.lock().unwrap(), buf, offset))
    }
}

impl WriteFileLike for RwLock<[u8]> {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Ok(do_write_at(&mut *self.write().unwrap(), buf, offset))
    }
}

/// Empty type for using with FilesystemFLRwOpen as the WriteLike and ReadWriteLike for readonly
/// fs-s (or the similar parallel for writeonly ones).
pub enum NoFile {}

#[allow(unused_variables)]
impl ReadFileLike for NoFile {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }
}

#[allow(unused_variables)]
impl WriteFileLike for NoFile {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        Err(libc::ENOSYS)
    }

    fn flush(&self) -> Result<()> {
        Err(libc::ENOSYS)
    }
}

/// Naive implementation of a read-write FileLike, given a read FileLike and
/// a write FileLike implementation.
pub struct ReadWriteAdaptor<R, W> {
    reader: R,
    writer: W,
}

impl<R, W> ReadFileLike for ReadWriteAdaptor<R, W> where R: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        self.reader.read_at(buf, offset)
    }
}

impl<R, W> WriteFileLike for ReadWriteAdaptor<R, W> where W: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        self.writer.write_at(buf, offset)
    }

    fn flush(&self) -> Result<()> {
        self.writer.flush()
    }
}

/// Implementation of a FileLike which can be either read-only, write-only or read-write.
/// Implementes both the ReadLike and WriteLike, returning EBADF in case of trying to write to a
/// read-only file or vice-versa (just like you'd expect).
pub enum ModalFileLike<R, W, RW> {
    ReadOnly(R),
    WriteOnly(W),
    ReadWrite(RW),
}

use self::ModalFileLike::*;

impl<R, W, RW> ReadFileLike for ModalFileLike<R, W, RW> where
    R: ReadFileLike, RW: ReadFileLike {
    fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(ref r) => r.read_at(buf, offset),
            WriteOnly(_) => Err(libc::EBADF),
            ReadWrite(ref rw) => rw.read_at(buf, offset),
        }
    }
}

impl<R, W, RW> WriteFileLike for ModalFileLike<R, W, RW> where
    W: WriteFileLike, RW: WriteFileLike {
    fn write_at(&self, buf: &[u8], offset: u64) -> Result<usize> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.write_at(buf, offset),
            ReadWrite(ref rw) => rw.write_at(buf, offset),
        }
    }

    fn flush(&self) -> Result<()> {
        match *self {
            ReadOnly(_) => Err(libc::EBADF),
            WriteOnly(ref w) => w.flush(),
            ReadWrite(ref rw) => rw.flush(),
        }
    }
}

/// Trait for using different types for the read, write and read-write files. The read-write type
/// can be a ReadWriteAdaptor over the read and write ones.
/// Everything implementing this implements FilesystemFLOpen, dispatching the open and create calls
/// according to the ACCMODE to the open or create method for the right type of file.
/// NOTE: fsync_metadata is not dispatched, but rather gets an enum over the possible file types,
/// because persumably the synchronization of metadata is mostly unaffected by how the file was
/// opened.
pub trait FilesystemFLRwOpen {
    type ReadLike: ReadFileLike; // = NoFile;
    type WriteLike: WriteFileLike; // = NoFile;
    type ReadWriteLike: ReadFileLike+WriteFileLike; // = ReadWriteAdaptor<Self::ReadLike, Self::WriteLike>;

    fn open_read(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn open_write(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn open_readwrite(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_read(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadLike> {
        Err(libc::ENOSYS)
    }

    fn create_write(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::WriteLike> {
        Err(libc::ENOSYS)
    }

    fn create_readwrite(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::ReadWriteLike> {
        Err(libc::ENOSYS)
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &ModalFileLike<
        Self::ReadLike,
        Self::WriteLike,
        Self::ReadWriteLike,
    >) -> ResultEmpty {
        Err(libc::ENOSYS)
    }
}

/// Trait for standard usecase of FilesystemFL - open and create methods return a FileLike object,
/// which supports reading, writing and flushing of data, and then these calls are passed directly
/// to it. Once specialization (RFC #1210) lands, this will be integrated as a default impl of
/// FilesystemFL for types implementing this; for now, this repeats some methods from FilesystemFL,
/// and these should be manually called in the implementation of FilesystemFL.
// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
pub trait FilesystemFLOpen {
    type FileLike: ReadFileLike+WriteFileLike;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        Err(libc::ENOSYS)
    }

    fn read(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _size: u32) -> ResultData {
        let _size = _size as usize;
        let mut vec = Vec::<u8>::with_capacity(_size);
        unsafe { vec.set_len(_size) };
        let num_read = _fl.read_at(vec.as_mut_slice(), _offset)?;
        assert!(num_read <= _size);
        unsafe { vec.set_len(num_read) };
        Ok(vec)
    }

    fn write(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _offset: u64, _data: Vec<u8>, _flags: u32) -> ResultWrite {
        assert!(_data.len() <= u32::max_value() as usize);
        _fl.write_at(_data.as_slice(), _offset).map(|x| x as u32)
    }

    fn fsync(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike, _datasync: bool) -> ResultEmpty {
        _fl.flush()?;
        if !_datasync {
            self.fsync_metadata(_req, _path, _fl)
        } else {
            Ok(())
        }
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike) -> ResultEmpty {
        Err(libc::ENOSYS)
    }
}

// Part of this will become a default impl of FilesystemFL when RFC #1210 lands.
impl<T> FilesystemFLOpen for T where T: FilesystemFLRwOpen {
    type FileLike = ModalFileLike<
        <Self as FilesystemFLRwOpen>::ReadLike,
        <Self as FilesystemFLRwOpen>::WriteLike,
        <Self as FilesystemFLRwOpen>::ReadWriteLike,
    >;

    fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpenObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_open(self.open_read(_req, _path, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_open(self.open_write(_req, _path, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_open(self.open_readwrite(_req, _path, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    /// If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the
    /// mknod() and open() methods will be called instead.
    fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreateObj<Self::FileLike> {
        match _flags as i32 & libc::O_ACCMODE {
            libc::O_RDONLY => map_res_create(self.create_read(_req, _parent, _name, _mode, _flags), |fl| ReadOnly(fl)),
            libc::O_WRONLY => map_res_create(self.create_write(_req, _parent, _name, _mode, _flags), |fl| WriteOnly(fl)),
            libc::O_RDWR => map_res_create(self.create_readwrite(_req, _parent, _name, _mode, _flags), |fl| ReadWrite(fl)),
            _ => Err(libc::EINVAL),
        }
    }

    fn fsync_metadata(&self, _req: RequestInfo, _path: &Path, _fl: &Self::FileLike) -> ResultEmpty {
        FilesystemFLRwOpen::fsync_metadata(self, _req, _path, _fl)
    }
}

<|endoftext|>


<commit_before>use std::collections::HashMap;

use quote::Tokens;
use syn::{Body, Field, Ident, Variant, VariantData, MacroInput, Ty};

/// Provides the hook to expand `#[derive(From)]` into an implementation of `From`
pub fn expand(input: &MacroInput) -> Tokens {
    match input.body {
        Body::Struct(VariantData::Tuple(ref structs)) => {
            if structs.len() == 1 {
                newtype_from(input.ident.clone(), structs[0].ty.clone())
            }
            else {
                unreachable!()
            }
        }
        _ => unreachable!()
    }
}

fn newtype_from(new_type: Ident, old_type: Ty) -> Tokens {
    quote!{
        impl ::std::convert::From<#old_type> for #new_type {
            fn from(a: #old_type) -> #new_type {
                #new_type(a)
            }
        }
    }
}
<commit_msg>Macros 1.1: Add better errors<commit_after>use std::collections::HashMap;

use quote::Tokens;
use syn::{Body, Field, Ident, Variant, VariantData, MacroInput, Ty};

/// Provides the hook to expand `#[derive(From)]` into an implementation of `From`
pub fn expand(input: &MacroInput) -> Tokens {
    match input.body {
        Body::Struct(VariantData::Tuple(ref structs)) => {
            if structs.len() == 1 {
                newtype_from(input.ident.clone(), structs[0].ty.clone())
            }
            else {
                panic!("Only Tuple structs with a single field can derive From")
            }
        }
        _ => panic!("Only newtype structs can derive From")
    }
}

fn newtype_from(new_type: Ident, old_type: Ty) -> Tokens {
    quote!{
        impl ::std::convert::From<#old_type> for #new_type {
            fn from(a: #old_type) -> #new_type {
                #new_type(a)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::mem;
use std::os::windows::io::*;
use std::time::Duration;

use handle::Handle;
use winapi::*;
use kernel32::*;

pub struct CompletionPort {
    handle: Handle,
}

#[derive(Clone, Copy)]
pub struct CompletionStatus(OVERLAPPED_ENTRY);

impl CompletionPort {
    pub fn new(threads: u32) -> io::Result<CompletionPort> {
        let ret = unsafe {
            CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0 as *mut _,
                                   0, threads)
        };
        if ret.is_null() {
            Err(io::Error::last_os_error())
        } else {
            Ok(CompletionPort { handle: Handle::new(ret) })
        }
    }

    pub fn add<T: AsRawHandle>(&self, token: usize, t: &T) -> io::Result<()> {
        self._add(token, t.as_raw_handle())
    }

    fn _add(&self, token: usize, handle: HANDLE) -> io::Result<()> {
        let ret = unsafe {
            CreateIoCompletionPort(handle, self.handle.raw(),
                                   token as ULONG_PTR, 0)
        };
        if ret.is_null() {
            Err(io::Error::last_os_error())
        } else {
            debug_assert_eq!(ret, self.handle.raw());
            Ok(())
        }
    }

    pub fn get(&self, timeout: Option<Duration>) -> io::Result<CompletionStatus> {
        let mut bytes = 0;
        let mut token = 0;
        let mut overlapped = 0 as *mut _;
        let timeout = timeout.map(::dur2timeout).unwrap_or(INFINITE);
        let ret = unsafe {
            GetQueuedCompletionStatus(self.handle.raw(),
                                      &mut bytes,
                                      &mut token,
                                      &mut overlapped,
                                      timeout)
        };
        ::cvt(ret).map(|_| {
            CompletionStatus(OVERLAPPED_ENTRY {
                dwNumberOfBytesTransferred: bytes,
                lpCompletionKey: token,
                lpOverlapped: overlapped,
                Internal: 0,
            })
        })
    }

    pub fn get_many<'a>(&self,
                        list: &'a mut [CompletionStatus],
                        timeout: Option<Duration>)
                        -> io::Result<&'a mut [CompletionStatus]>
    {
        debug_assert_eq!(mem::size_of::<CompletionStatus>(),
                         mem::size_of::<OVERLAPPED_ENTRY>());
        let mut removed = 0;
        let timeout = timeout.map(::dur2timeout).unwrap_or(INFINITE);
        let ret = unsafe {
            GetQueuedCompletionStatusEx(self.handle.raw(),
                                        list.as_ptr() as *mut _,
                                        list.len() as ULONG,
                                        &mut removed,
                                        timeout,
                                        FALSE)
        };
        match ::cvt(ret) {
            Ok(_) => Ok(&mut list[..removed as usize]),
            Err(e) => Err(e),
        }
    }

    pub fn post(&self, status: CompletionStatus) -> io::Result<()> {
        let ret = unsafe {
            PostQueuedCompletionStatus(self.handle.raw(),
                                       status.0.dwNumberOfBytesTransferred,
                                       status.0.lpCompletionKey,
                                       status.0.lpOverlapped)
        };
        ::cvt(ret).map(|_| ())
    }
}

impl AsRawHandle for CompletionPort {
    fn as_raw_handle(&self) -> HANDLE {
        self.handle.raw()
    }
}

impl FromRawHandle for CompletionPort {
    unsafe fn from_raw_handle(handle: HANDLE) -> CompletionPort {
        CompletionPort { handle: Handle::new(handle) }
    }
}

#[cfg(feature = "unstable")]
impl IntoRawHandle for CompletionPort {
    fn into_raw_handle(self) -> HANDLE {
        self.handle.into_raw()
    }
}

impl CompletionStatus {
    pub fn new(bytes: u32, token: usize, overlapped: *mut OVERLAPPED)
               -> CompletionStatus {
        CompletionStatus(OVERLAPPED_ENTRY {
            dwNumberOfBytesTransferred: bytes,
            lpCompletionKey: token as ULONG_PTR,
            lpOverlapped: overlapped,
            Internal: 0,
        })
    }

    pub fn zero() -> CompletionStatus {
        CompletionStatus::new(0, 0, 0 as *mut _)
    }

    pub fn bytes_transferred(&self) -> u32 {
        self.0.dwNumberOfBytesTransferred
    }

    pub fn token(&self) -> usize {
        self.0.lpCompletionKey as usize
    }

    pub fn overlapped(&self) -> *mut OVERLAPPED {
        self.0.lpOverlapped
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use winapi::*;

    #[test]
    fn token_right_size() {
        assert_eq!(mem::size_of::<usize>(), mem::size_of::<ULONG_PTR>());
    }
}
<commit_msg>Add some tests<commit_after>use std::io;
use std::mem;
use std::os::windows::io::*;
use std::time::Duration;

use handle::Handle;
use winapi::*;
use kernel32::*;

pub struct CompletionPort {
    handle: Handle,
}

#[derive(Clone, Copy, Debug)]
pub struct CompletionStatus(OVERLAPPED_ENTRY);

impl CompletionPort {
    pub fn new(threads: u32) -> io::Result<CompletionPort> {
        let ret = unsafe {
            CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0 as *mut _,
                                   0, threads)
        };
        if ret.is_null() {
            Err(io::Error::last_os_error())
        } else {
            Ok(CompletionPort { handle: Handle::new(ret) })
        }
    }

    pub fn add<T: AsRawHandle>(&self, token: usize, t: &T) -> io::Result<()> {
        self._add(token, t.as_raw_handle())
    }

    fn _add(&self, token: usize, handle: HANDLE) -> io::Result<()> {
        let ret = unsafe {
            CreateIoCompletionPort(handle, self.handle.raw(),
                                   token as ULONG_PTR, 0)
        };
        if ret.is_null() {
            Err(io::Error::last_os_error())
        } else {
            debug_assert_eq!(ret, self.handle.raw());
            Ok(())
        }
    }

    pub fn get(&self, timeout: Option<Duration>) -> io::Result<CompletionStatus> {
        let mut bytes = 0;
        let mut token = 0;
        let mut overlapped = 0 as *mut _;
        let timeout = timeout.map(::dur2timeout).unwrap_or(INFINITE);
        let ret = unsafe {
            GetQueuedCompletionStatus(self.handle.raw(),
                                      &mut bytes,
                                      &mut token,
                                      &mut overlapped,
                                      timeout)
        };
        ::cvt(ret).map(|_| {
            CompletionStatus(OVERLAPPED_ENTRY {
                dwNumberOfBytesTransferred: bytes,
                lpCompletionKey: token,
                lpOverlapped: overlapped,
                Internal: 0,
            })
        })
    }

    pub fn get_many<'a>(&self,
                        list: &'a mut [CompletionStatus],
                        timeout: Option<Duration>)
                        -> io::Result<&'a mut [CompletionStatus]>
    {
        debug_assert_eq!(mem::size_of::<CompletionStatus>(),
                         mem::size_of::<OVERLAPPED_ENTRY>());
        let mut removed = 0;
        let timeout = timeout.map(::dur2timeout).unwrap_or(INFINITE);
        let ret = unsafe {
            GetQueuedCompletionStatusEx(self.handle.raw(),
                                        list.as_ptr() as *mut _,
                                        list.len() as ULONG,
                                        &mut removed,
                                        timeout,
                                        FALSE)
        };
        match ::cvt(ret) {
            Ok(_) => Ok(&mut list[..removed as usize]),
            Err(e) => Err(e),
        }
    }

    pub fn post(&self, status: CompletionStatus) -> io::Result<()> {
        let ret = unsafe {
            PostQueuedCompletionStatus(self.handle.raw(),
                                       status.0.dwNumberOfBytesTransferred,
                                       status.0.lpCompletionKey,
                                       status.0.lpOverlapped)
        };
        ::cvt(ret).map(|_| ())
    }
}

impl AsRawHandle for CompletionPort {
    fn as_raw_handle(&self) -> HANDLE {
        self.handle.raw()
    }
}

impl FromRawHandle for CompletionPort {
    unsafe fn from_raw_handle(handle: HANDLE) -> CompletionPort {
        CompletionPort { handle: Handle::new(handle) }
    }
}

#[cfg(feature = "unstable")]
impl IntoRawHandle for CompletionPort {
    fn into_raw_handle(self) -> HANDLE {
        self.handle.into_raw()
    }
}

impl CompletionStatus {
    pub fn new(bytes: u32, token: usize, overlapped: *mut OVERLAPPED)
               -> CompletionStatus {
        CompletionStatus(OVERLAPPED_ENTRY {
            dwNumberOfBytesTransferred: bytes,
            lpCompletionKey: token as ULONG_PTR,
            lpOverlapped: overlapped,
            Internal: 0,
        })
    }

    pub fn zero() -> CompletionStatus {
        CompletionStatus::new(0, 0, 0 as *mut _)
    }

    pub fn bytes_transferred(&self) -> u32 {
        self.0.dwNumberOfBytesTransferred
    }

    pub fn token(&self) -> usize {
        self.0.lpCompletionKey as usize
    }

    pub fn overlapped(&self) -> *mut OVERLAPPED {
        self.0.lpOverlapped
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use std::time::Duration;
    use winapi::*;

    use {CompletionPort, CompletionStatus};

    #[test]
    fn token_right_size() {
        assert_eq!(mem::size_of::<usize>(), mem::size_of::<ULONG_PTR>());
    }

    #[test]
    fn timeout() {
        let c = CompletionPort::new(1).unwrap();
        let err = c.get(Some(Duration::new(0, 1_000_000))).unwrap_err();
        assert_eq!(err.raw_os_error(), Some(WAIT_TIMEOUT as i32));
    }

    #[test]
    fn get() {
        let c = CompletionPort::new(1).unwrap();
        c.post(CompletionStatus::new(1, 2, 3 as *mut _)).unwrap();
        let s = c.get(None).unwrap();
        assert_eq!(s.bytes_transferred(), 1);
        assert_eq!(s.token(), 2);
        assert_eq!(s.overlapped(), 3 as *mut _);
    }

    #[test]
    fn get_many() {
        let c = CompletionPort::new(1).unwrap();

        c.post(CompletionStatus::new(1, 2, 3 as *mut _)).unwrap();
        c.post(CompletionStatus::new(4, 5, 6 as *mut _)).unwrap();

        let mut s = vec![CompletionStatus::zero(); 4];
        {
            let s = c.get_many(&mut s, None).unwrap();
            assert_eq!(s.len(), 2);
            assert_eq!(s[0].bytes_transferred(), 1);
            assert_eq!(s[0].token(), 2);
            assert_eq!(s[0].overlapped(), 3 as *mut _);
            assert_eq!(s[1].bytes_transferred(), 4);
            assert_eq!(s[1].token(), 5);
            assert_eq!(s[1].overlapped(), 6 as *mut _);
        }
        assert_eq!(s[2].bytes_transferred(), 0);
        assert_eq!(s[2].token(), 0);
        assert_eq!(s[2].overlapped(), 0 as *mut _);
    }
}
<|endoftext|>


<commit_before>#![feature(plugin, box_syntax)]
#![plugin(clippy)]
#![allow(warnings, clippy)]

#![deny(boxed_local)]

#[derive(Clone)]
struct A;

impl A {
    fn foo(&self){}
}

trait Z {
    fn bar(&self);
}

impl Z for A {
    fn bar(&self) {
        //nothing
    }
}

fn main() {
}

fn ok_box_trait(boxed_trait: &Box<Z>) {
    let boxed_local = boxed_trait;
    // done
}

fn warn_call() {
    let x = box A; //~ ERROR local variable
    x.foo();
}

fn warn_arg(x: Box<A>) { //~ ERROR local variable
    x.foo();
}

fn nowarn_closure_arg() {
    let x = Some(box A);
    x.map_or((), |x| take_ref(&x));
}

fn warn_rename_call() {
    let x = box A;

    let y = x; //~ ERROR local variable
    y.foo(); // via autoderef
}

fn warn_notuse() {
    let bz = box A; //~ ERROR local variable
}

fn warn_pass() {
    let bz = box A; //~ ERROR local variable
    take_ref(&bz); // via deref coercion
}

fn nowarn_return() -> Box<A> {
    let fx = box A;
    fx // moved out, "escapes"
}

fn nowarn_move() {
    let bx = box A;
    drop(bx) // moved in, "escapes"
}
fn nowarn_call() {
    let bx = box A;
    bx.clone(); // method only available to Box, not via autoderef
}

fn nowarn_pass() {
    let bx = box A;
    take_box(&bx); // fn needs &Box
}

fn take_box(x: &Box<A>) {}
fn take_ref(x: &A) {}

fn nowarn_ref_take() {
    // false positive, should actually warn
    let x = box A; //~ ERROR local variable
    let y = &x;
    take_box(y);
}

fn nowarn_match() {
    let x = box A; // moved into a match
    match x {
        y => drop(y)
    }
}

fn warn_match() {
    let x = box A; //~ ERROR local variable
    match &x { // not moved
        ref y => ()
    }
}

fn nowarn_large_array() {
    // should not warn, is large array
    // and should not be on stack
    let x = box [1; 10000];
    match &x { // not moved
        ref y => ()
    }
}
<commit_msg>Remove EOL space<commit_after>#![feature(plugin, box_syntax)]
#![plugin(clippy)]
#![allow(warnings, clippy)]

#![deny(boxed_local)]

#[derive(Clone)]
struct A;

impl A {
    fn foo(&self){}
}

trait Z {
    fn bar(&self);
}

impl Z for A {
    fn bar(&self) {
        //nothing
    }
}

fn main() {
}

fn ok_box_trait(boxed_trait: &Box<Z>) {
    let boxed_local = boxed_trait;
    // done
}

fn warn_call() {
    let x = box A; //~ ERROR local variable
    x.foo();
}

fn warn_arg(x: Box<A>) { //~ ERROR local variable
    x.foo();
}

fn nowarn_closure_arg() {
    let x = Some(box A);
    x.map_or((), |x| take_ref(&x));
}

fn warn_rename_call() {
    let x = box A;

    let y = x; //~ ERROR local variable
    y.foo(); // via autoderef
}

fn warn_notuse() {
    let bz = box A; //~ ERROR local variable
}

fn warn_pass() {
    let bz = box A; //~ ERROR local variable
    take_ref(&bz); // via deref coercion
}

fn nowarn_return() -> Box<A> {
    let fx = box A;
    fx // moved out, "escapes"
}

fn nowarn_move() {
    let bx = box A;
    drop(bx) // moved in, "escapes"
}
fn nowarn_call() {
    let bx = box A;
    bx.clone(); // method only available to Box, not via autoderef
}

fn nowarn_pass() {
    let bx = box A;
    take_box(&bx); // fn needs &Box
}

fn take_box(x: &Box<A>) {}
fn take_ref(x: &A) {}

fn nowarn_ref_take() {
    // false positive, should actually warn
    let x = box A; //~ ERROR local variable
    let y = &x;
    take_box(y);
}

fn nowarn_match() {
    let x = box A; // moved into a match
    match x {
        y => drop(y)
    }
}

fn warn_match() {
    let x = box A; //~ ERROR local variable
    match &x { // not moved
        ref y => ()
    }
}

fn nowarn_large_array() {
    // should not warn, is large array
    // and should not be on stack
    let x = box [1; 10000];
    match &x { // not moved
        ref y => ()
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate clap;

extern crate qrcode;
extern crate image;
extern crate regex;
extern crate urlparse;
extern crate term;

use clap::{App, AppSettings, Arg, Shell, SubCommand};
use qrcode::{QrCode, EcLevel};
use term::color;
use image::Luma;
use std::io::prelude::*;
use std::process::exit;
use std::fs;

mod payloads;

const WIFI_COMMAND: &'static str = "wifi";
const MAIL_COMMAND: &'static str = "mail";
const SMS_COMMAND: &'static str = "sms";
const MMS_COMMAND: &'static str = "mms";
const GEO_COMMAND: &'static str = "geo";
const PHONE_COMMAND: &'static str = "phone";
const SKYPE_COMMAND: &'static str = "skype";
const WHATSAPP_COMMAND: &'static str = "whatsapp";
const URL_COMMAND: &'static str = "url";
const BOOKMARK_COMMAND: &'static str = "bookmark";
const BITCOIN_COMMAND: &'static str = "bitcoin";
// const GIRO_COMMAND: &'static str = "giro";
// const CALENDAR_COMMAND: &'static str = "calendar";

/*TODO: add arguments for:
- Add help texts for the subcommands
- Add error texts for some exit branches (mostly file output and qrcode gen)
- implement different types of qr payloads
- write unit tests for the payloads
- write integration tests for edge case inputs
*/
fn main() {
    // match all input args
    let matches = build_cli().get_matches();

    // write the completions if they were requested, then exit and dont print any qr-code
    if let Some(comp) = matches.subcommand_matches("completions") {
        let dir = comp.value_of("comp_dir").unwrap();

        let shell = match comp.value_of("shell") {
            Some("ps") => Shell::PowerShell,
            Some("zsh") => Shell::Zsh,
            Some("fish") => Shell::Fish,
            _ => Shell::Bash,
        };

        // create directory if necessary
        fs::create_dir_all(&dir).unwrap();

        build_cli().gen_completions("qr", shell, dir);

        println!(
            "Completion file for the {:?} shell was writen to: {:?}",
            shell,
            dir
        );

        // exit and dont generate a qr-code
        exit(0);
    }

    // deduce the string payload
    let payload = get_payload(&matches);

    // should we draw a white border (safe zone) around the code?
    let safe = match matches.occurrences_of("safe_zone") {
        0 => true,
        _ => false,
    };

    // what error level can we expect? always defaults to "H"
    let error: EcLevel = match matches.value_of("error").unwrap() {
        "L" => EcLevel::L,
        "M" => EcLevel::M,
        "Q" => EcLevel::Q,
        &_ => EcLevel::H,
    };

    //TODO: catch the possible errors
    let code = QrCode::with_error_correction_level(&payload, error).unwrap();

    // are we drawing to the terminal or to a file?
    match matches.occurrences_of("output") {
        1 => save(&code, safe, matches.value_of("output").unwrap()),
        _ => draw(&code, safe),
    }

    // shall we also print the payload to the screen?
    if matches.occurrences_of("payload") > 0 {
        println!("{:?}", payload);
    }
}

// deduces wich kind of string we are going to encode
fn get_payload(matches: &clap::ArgMatches) -> String {
    if let Some(sub) = matches.subcommand_matches(WIFI_COMMAND) {
        let auth = match sub.value_of("mode") {
            Some("WEP") => payloads::Authentication::WEP,
            Some("WPA") => payloads::Authentication::WPA,
            _ => payloads::Authentication::nopass,
        };
        return payloads::wifi_string(
            sub.value_of("ssid").unwrap(),
            sub.value_of("pwd").unwrap(),
            &auth,
            sub.value_of("hidden").unwrap() == "true",
        );
    } else if let Some(sub) = matches.subcommand_matches(MAIL_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("MATMSG") => payloads::MailEncoding::MATMSG,
            Some("SMTP") => payloads::MailEncoding::SMTP,
            _ => payloads::MailEncoding::MAILTO,
        };
        return payloads::mail_string(
            sub.value_of("receiver").unwrap(),
            sub.value_of("subject").unwrap(),
            sub.value_of("message").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(URL_COMMAND) {
        return payloads::url_string(sub.value_of("url").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(PHONE_COMMAND) {
        return payloads::phone_string(sub.value_of("phone").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(SKYPE_COMMAND) {
        return payloads::skype_string(sub.value_of("name").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(WHATSAPP_COMMAND) {
        return payloads::whatsapp_string(sub.value_of("message").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(SMS_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("SMSTO") => payloads::SMSEncoding::SMSTO,
            Some("SMS_iOS") => payloads::SMSEncoding::SMS_iOS,
            _ => payloads::SMSEncoding::SMS,
        };
        return payloads::sms_string(
            sub.value_of("number").unwrap(),
            sub.value_of("subject").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(MMS_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("MMSTO") => payloads::MMSEncoding::MMSTO,
            _ => payloads::MMSEncoding::MMS,
        };
        return payloads::mms_string(
            sub.value_of("number").unwrap(),
            sub.value_of("subject").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(GEO_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("GoogleMaps") => payloads::GeolocationEncoding::GoogleMaps,
            _ => payloads::GeolocationEncoding::GEO,
        };
        return payloads::geo_string(
            sub.value_of("latitude").unwrap(),
            sub.value_of("longitude").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(BOOKMARK_COMMAND) {
        return payloads::bookmark_string(
            sub.value_of("title").unwrap(),
            sub.value_of("url").unwrap(),
        );
    } else if let Some(sub) = matches.subcommand_matches(BITCOIN_COMMAND) {
        return payloads::bitcoin_string(
            sub.value_of("address").unwrap(),
            sub.value_of("amount").map(|a| a.parse::<f64>().unwrap_or_default()),
            sub.value_of("label"),
            sub.value_of("message"),
        );
    } else {
        return String::from(matches.value_of("INPUT").unwrap());
    }
}

// save to a file at the path
fn save(code: &QrCode, safe: bool, path: &str) {
    // render to a image struct
    let image = code.render::<Luma<u8>>().quiet_zone(safe).build();

    // save the image
    match image.save(path) {
        Ok(..) => println!("Image successfully saved to: {:?}", path),
        Err(e) => {
            println!(
                "Tried to create file but there was a problem: {:?}",
                if let Some(inner_err) = e.get_ref() {
                    inner_err.to_string()
                } else {
                    e.to_string()
                }
            )
        }
    };
}

// draw to the terminal
fn draw(code: &QrCode, safe: bool) {
    // get "bit" array
    let bit_array = code.to_colors();

    // get the terminal output pipe
    let mut t = term::stdout().unwrap();

    // get the code width and add extra space for the safe zone
    let w = code.width();
    let wide = w + 6;

    // draw the first white safe zone
    if safe {
        for a in 1..(wide * 3) + 1 {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "  ").unwrap();
            if a % wide == 0 {
                t.reset().unwrap();
                writeln!(t, "").unwrap();
            }
        }
    }

    // main drawing loop
    for (i, item) in bit_array.iter().enumerate() {
        // left safe zone
        if safe && i % w == 0 {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "      ").unwrap();
        }

        // draw black or white blocks
        if *item == qrcode::Color::Dark {
            t.bg(color::BLACK).unwrap();
            write!(t, "  ").unwrap();
        } else {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "  ").unwrap();
        }

        if (i + 1) % w == 0 {
            if safe {
                // draw right safe zone
                t.bg(color::BRIGHT_WHITE).unwrap();
                write!(t, "      ").unwrap();
            }
            t.reset().unwrap();
            writeln!(t, "").unwrap();
        }
    }

    // draw the last white safe zone
    if safe {
        for a in 1..(wide * 3) + 1 {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "  ").unwrap();
            if a % wide == 0 {
                t.reset().unwrap();
                writeln!(t, "").unwrap();
            }
        }
    }

    // reset to normal color and flush write buffer
    t.reset().unwrap();
    t.flush().unwrap();
}

// create the interface for the app with all subcommands, flags and args
fn build_cli() -> App<'static, 'static> {
    App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .about(crate_description!())
        .global_setting(AppSettings::SubcommandsNegateReqs)
        .arg(
            Arg::with_name("safe_zone")
                .global(true)
                .short("s")
                .long("safe-zone")
                .help(
                    "Sets wether the safe zone around the code should be drawn or not.",
                )
                .takes_value(false),
        )
        .arg(
            Arg::with_name("output")
                .global(true)
                .short("o")
                .long("output")
                .help(
                    "Prints the QR-Code to a file.
            The image format is derived from the file extension.
            Currently only jpeg and png files are supported.",
                )
                .value_name("FILE"),
        )
        .arg(
            Arg::with_name("payload")
                .global(true)
                .short("p")
                .long("payload")
                .help(
                    "If this flag is set, the generated payload will also be printed to the terminal."
                )
                .takes_value(false)
        )
        .arg(
            Arg::with_name("error")
                .global(true)
                .short("e")
                .long("error")
                .help("Set the desired error correction level.")
                .value_name("LEVEL")
                .possible_values(&["L", "M", "Q", "H"])
                .default_value("H"),
        )
        .arg(
            Arg::with_name("INPUT")
                .help("The input string to use")
                .required(true),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Outputs completion files for various shells.")
                .arg(
                    Arg::with_name("comp_dir")
                        .required(true)
                        .help("The directory to write the completion file to.")
                        .value_name("DIR"),
                )
                .arg(
                    Arg::with_name("shell")
                        .long("shell")
                        .help("For which shell the completions should be generated for.")
                        .value_name("SHELL")
                        .possible_values(&["bash", "zsh", "fish", "ps"])
                        .default_value("bash"),
                ),
        )
        .subcommand(
            SubCommand::with_name(WIFI_COMMAND)
                .about("formats to a wifi access string QR-Code")
                .arg(Arg::with_name("ssid").required(true))
                .arg(Arg::with_name("pwd").required(true))
                .arg(
                    Arg::with_name("mode")
                        .value_name("MODE")
                        .possible_values(&["WEP", "WPA", "nopass"])
                        .default_value("WPA"),
                )
                .arg(
                    Arg::with_name("hidden")
                        .value_name("HIDDEN")
                        .possible_values(&["true", "false"])
                        .default_value("false"),
                ),
        )
        .subcommand(
            SubCommand::with_name(MAIL_COMMAND)
                .about("formats to a mail adress string QR-Code")
                .arg(Arg::with_name("receiver").required(true))
                .arg(Arg::with_name("subject"))
                .arg(Arg::with_name("message"))
                .arg(
                    Arg::with_name("encoding")
                        .value_name("ENCODING")
                        .possible_values(&["MAILTO", "MATMSG", "SMTP"])
                        .default_value("MAILTO"),
                ),
        )
        .subcommand(
            SubCommand::with_name(URL_COMMAND)
                .about("formats to an URL QR-Code")
                .arg(Arg::with_name("url").required(true).value_name("URL")),
        )
        .subcommand(
            SubCommand::with_name(PHONE_COMMAND)
                .about("formats to a phone number QR-Code")
                .arg(Arg::with_name("number").required(true).value_name("NUMBER")),
        )
        .subcommand(
            SubCommand::with_name(SKYPE_COMMAND)
                .about("formats to a skype call QR-Code")
                .arg(Arg::with_name("name").required(true).value_name("HANDLE")),
        )
        .subcommand(
            SubCommand::with_name(WHATSAPP_COMMAND)
                .about("formats to a whatsapp message QR-Code")
                .arg(Arg::with_name("message").required(true).value_name(
                    "MESSAGE",
                )),
        )
        .subcommand(
            SubCommand::with_name(SMS_COMMAND)
                .about("formats to a sms message QR-Code")
                .arg(Arg::with_name("number").required(true))
                .arg(Arg::with_name("subject").default_value(""))
                .arg(
                    Arg::with_name("encoding")
                        .possible_values(&["SMS", "SMSTO", "SMS_iOS"])
                        .default_value("SMS"),
                ),
        )
        .subcommand(
            SubCommand::with_name(MMS_COMMAND)
                .about("formats to a mms message QR-Code")
                .arg(Arg::with_name("number").required(true))
                .arg(Arg::with_name("subject").default_value(""))
                .arg(
                    Arg::with_name("encoding")
                        .possible_values(&["MMS", "MMSTO"])
                        .default_value("MMS"),
                ),
        )
        .subcommand(
            SubCommand::with_name(GEO_COMMAND)
                .about("formats to a geospacial location QR-Code")
                .arg(Arg::with_name("latitude").required(true))
                .arg(Arg::with_name("longitude").required(true))
                .arg(
                    Arg::with_name("encoding")
                        .possible_values(&["GEO", "GoogleMaps"])
                        .default_value("GEO"),
                ),
        )
        .subcommand(
            SubCommand::with_name(BOOKMARK_COMMAND)
                .about("formats to a bookmark QR-Code")
                .arg(Arg::with_name("title").required(true))
                .arg(Arg::with_name("url").required(true)),
        )
        .subcommand(
            SubCommand::with_name(BITCOIN_COMMAND)
                .about("outputs a bitcoin adress/transaction")
                .arg(Arg::with_name("address").required(true))
                .arg(Arg::with_name("amount"))
                .arg(Arg::with_name("label"))
                .arg(Arg::with_name("message")),
        )
}
<commit_msg>some small edits<commit_after>#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate clap;

extern crate qrcode;
extern crate image;
extern crate regex;
extern crate urlparse;
extern crate term;

use clap::{App, AppSettings, Arg, Shell, SubCommand};
use qrcode::{QrCode, EcLevel};
use term::color;
use image::Luma;
use std::io::prelude::*;
use std::process::exit;
use std::fs;

mod payloads;

const WIFI_COMMAND: &'static str = "wifi";
const MAIL_COMMAND: &'static str = "mail";
const SMS_COMMAND: &'static str = "sms";
const MMS_COMMAND: &'static str = "mms";
const GEO_COMMAND: &'static str = "geo";
const PHONE_COMMAND: &'static str = "phone";
const SKYPE_COMMAND: &'static str = "skype";
const WHATSAPP_COMMAND: &'static str = "whatsapp";
const URL_COMMAND: &'static str = "url";
const BOOKMARK_COMMAND: &'static str = "bookmark";
const BITCOIN_COMMAND: &'static str = "bitcoin";
// const GIRO_COMMAND: &'static str = "giro";
// const CALENDAR_COMMAND: &'static str = "calendar";
// const CONTACT_COMMAND: &'static str = "contact";

/*TODO: add arguments for:
- Add help texts for the subcommands
- Add error texts for some exit branches (mostly file output and qrcode gen)
- implement different types of qr payloads
- write unit tests for the payloads
- write integration tests for edge case inputs
*/
fn main() {
    // match all input args
    let matches = build_cli().get_matches();

    // write the completions if they were requested, then exit and dont print any qr-code
    if let Some(comp) = matches.subcommand_matches("completions") {
        let dir = comp.value_of("comp_dir").unwrap();

        let shell = match comp.value_of("shell") {
            Some("ps") => Shell::PowerShell,
            Some("zsh") => Shell::Zsh,
            Some("fish") => Shell::Fish,
            _ => Shell::Bash,
        };

        // create directory if necessary
        fs::create_dir_all(&dir).unwrap();

        build_cli().gen_completions("qr", shell, dir);

        println!(
            "Completion file for the {:?} shell was writen to: {:?}",
            shell,
            dir
        );

        // exit and dont generate a qr-code
        exit(0);
    }

    // deduce the string payload
    let payload = get_payload(&matches);

    // should we draw a white border (safe zone) around the code?
    let safe = match matches.occurrences_of("safe_zone") {
        0 => true,
        _ => false,
    };

    // what error level can we expect? defaults to "H"
    let error: EcLevel = match matches.value_of("error").unwrap() {
        "L" => EcLevel::L,
        "M" => EcLevel::M,
        "Q" => EcLevel::Q,
        &_ => EcLevel::H,
    };

    //TODO: catch the possible errors
    let code = QrCode::with_error_correction_level(&payload, error).unwrap();

    // are we drawing to the terminal or to a file?
    match matches.occurrences_of("output") {
        1 => save(&code, safe, matches.value_of("output").unwrap()),
        _ => draw(&code, safe),
    }

    // shall we also print the payload to the screen?
    if matches.occurrences_of("payload") > 0 {
        println!("{:?}", payload);
    }
}

// deduces wich kind of string we are going to encode
fn get_payload(matches: &clap::ArgMatches) -> String {
    if let Some(sub) = matches.subcommand_matches(WIFI_COMMAND) {
        let auth = match sub.value_of("mode") {
            Some("WEP") => payloads::Authentication::WEP,
            Some("WPA") => payloads::Authentication::WPA,
            _ => payloads::Authentication::nopass,
        };
        return payloads::wifi_string(
            sub.value_of("ssid").unwrap(),
            sub.value_of("pwd").unwrap(),
            &auth,
            sub.value_of("hidden").unwrap() == "true",
        );
    } else if let Some(sub) = matches.subcommand_matches(MAIL_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("MATMSG") => payloads::MailEncoding::MATMSG,
            Some("SMTP") => payloads::MailEncoding::SMTP,
            _ => payloads::MailEncoding::MAILTO,
        };
        return payloads::mail_string(
            sub.value_of("receiver").unwrap(),
            sub.value_of("subject").unwrap(),
            sub.value_of("message").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(URL_COMMAND) {
        return payloads::url_string(sub.value_of("url").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(PHONE_COMMAND) {
        return payloads::phone_string(sub.value_of("phone").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(SKYPE_COMMAND) {
        return payloads::skype_string(sub.value_of("name").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(WHATSAPP_COMMAND) {
        return payloads::whatsapp_string(sub.value_of("message").unwrap());
    } else if let Some(sub) = matches.subcommand_matches(SMS_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("SMSTO") => payloads::SMSEncoding::SMSTO,
            Some("SMS_iOS") => payloads::SMSEncoding::SMS_iOS,
            _ => payloads::SMSEncoding::SMS,
        };
        return payloads::sms_string(
            sub.value_of("number").unwrap(),
            sub.value_of("subject").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(MMS_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("MMSTO") => payloads::MMSEncoding::MMSTO,
            _ => payloads::MMSEncoding::MMS,
        };
        return payloads::mms_string(
            sub.value_of("number").unwrap(),
            sub.value_of("subject").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(GEO_COMMAND) {
        let encoding = match sub.value_of("encoding") {
            Some("GoogleMaps") => payloads::GeolocationEncoding::GoogleMaps,
            _ => payloads::GeolocationEncoding::GEO,
        };
        return payloads::geo_string(
            sub.value_of("latitude").unwrap(),
            sub.value_of("longitude").unwrap(),
            &encoding,
        );
    } else if let Some(sub) = matches.subcommand_matches(BOOKMARK_COMMAND) {
        return payloads::bookmark_string(
            sub.value_of("title").unwrap(),
            sub.value_of("url").unwrap(),
        );
    } else if let Some(sub) = matches.subcommand_matches(BITCOIN_COMMAND) {
        return payloads::bitcoin_string(
            sub.value_of("address").unwrap(),
            sub.value_of("amount").map(|a| a.parse::<f64>().unwrap_or_default()),
            sub.value_of("label"),
            sub.value_of("message"),
        );
    } else {
        return String::from(matches.value_of("INPUT").unwrap());
    }
}

// save to a file at the path
fn save(code: &QrCode, safe: bool, path: &str) {
    // render to a image struct
    let image = code.render::<Luma<u8>>().quiet_zone(safe).build();

    // save the image
    match image.save(path) {
        Ok(..) => println!("Image successfully saved to: {:?}", path),
        Err(e) => {
            println!(
                "Tried to create file but there was a problem: {:?}",
                if let Some(inner_err) = e.get_ref() {
                    inner_err.to_string()
                } else {
                    e.to_string()
                }
            )
        }
    };
}

// draw to the terminal
fn draw(code: &QrCode, safe: bool) {
    // get "bit" array
    let bit_array = code.to_colors();

    // get the terminal output pipe
    let mut t = term::stdout().unwrap();

    // get the code width and add extra space for the safe zone
    let w = code.width();
    let wide = w + 6;

    // draw the first white safe zone
    if safe {
        for a in 1..(wide * 3) + 1 {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "  ").unwrap();
            if a % wide == 0 {
                t.reset().unwrap();
                writeln!(t, "").unwrap();
            }
        }
    }

    // main drawing loop
    for (i, item) in bit_array.iter().enumerate() {
        // left safe zone
        if safe && i % w == 0 {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "      ").unwrap();
        }

        // draw black or white blocks
        if *item == qrcode::Color::Dark {
            t.bg(color::BLACK).unwrap();
            write!(t, "  ").unwrap();
        } else {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "  ").unwrap();
        }

        if (i + 1) % w == 0 {
            if safe {
                // draw right safe zone
                t.bg(color::BRIGHT_WHITE).unwrap();
                write!(t, "      ").unwrap();
            }
            t.reset().unwrap();
            writeln!(t, "").unwrap();
        }
    }

    // draw the last white safe zone
    if safe {
        for a in 1..(wide * 3) + 1 {
            t.bg(color::BRIGHT_WHITE).unwrap();
            write!(t, "  ").unwrap();
            if a % wide == 0 {
                t.reset().unwrap();
                writeln!(t, "").unwrap();
            }
        }
    }

    // reset to normal color and flush write buffer
    t.reset().unwrap();
    t.flush().unwrap();
}

// create the interface for the app with all subcommands, flags and args
fn build_cli() -> App<'static, 'static> {
    App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .about(crate_description!())
        .global_setting(AppSettings::SubcommandsNegateReqs)
        .arg(
            Arg::with_name("safe_zone")
                .global(true)
                .short("s")
                .long("safe-zone")
                .help(
                    "Sets wether the safe zone around the code should be drawn or not.",
                )
                .takes_value(false),
        )
        .arg(
            Arg::with_name("output")
                .global(true)
                .short("o")
                .long("output")
                .help(
                    "Prints the QR-Code to a file.
            The image format is derived from the file extension.
            Currently only jpeg and png files are supported.",
                )
                .value_name("FILE"),
        )
        .arg(
            Arg::with_name("payload")
                .global(true)
                .short("p")
                .long("payload")
                .help(
                    "If this flag is set, the generated payload will also be printed to the terminal."
                )
                .takes_value(false)
        )
        .arg(
            Arg::with_name("error")
                .global(true)
                .short("e")
                .long("error")
                .help("Set the desired error correction level.")
                .value_name("LEVEL")
                .possible_values(&["L", "M", "Q", "H"])
                .default_value("H"),
        )
        .arg(
            Arg::with_name("INPUT")
                .help("The input string to use")
                .required(true),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Outputs completion files for various shells.")
                .arg(
                    Arg::with_name("comp_dir")
                        .required(true)
                        .help("The directory to write the completion file to.")
                        .value_name("DIR"),
                )
                .arg(
                    Arg::with_name("shell")
                        .long("shell")
                        .help("For which shell the completions should be generated for.")
                        .value_name("SHELL")
                        .possible_values(&["bash", "zsh", "fish", "ps"])
                        .default_value("bash"),
                ),
        )
        .subcommand(
            SubCommand::with_name(WIFI_COMMAND)
                .about("formats to a wifi access string QR-Code")
                .arg(Arg::with_name("ssid").required(true))
                .arg(Arg::with_name("pwd").required(true))
                .arg(
                    Arg::with_name("mode")
                        .value_name("MODE")
                        .possible_values(&["WEP", "WPA", "nopass"])
                        .default_value("WPA"),
                )
                .arg(
                    Arg::with_name("hidden")
                        .value_name("HIDDEN")
                        .possible_values(&["true", "false"])
                        .default_value("false"),
                ),
        )
        .subcommand(
            SubCommand::with_name(MAIL_COMMAND)
                .about("formats to a mail adress string QR-Code")
                .arg(Arg::with_name("receiver").required(true))
                .arg(Arg::with_name("subject"))
                .arg(Arg::with_name("message"))
                .arg(
                    Arg::with_name("encoding")
                        .value_name("ENCODING")
                        .possible_values(&["MAILTO", "MATMSG", "SMTP"])
                        .default_value("MAILTO"),
                ),
        )
        .subcommand(
            SubCommand::with_name(URL_COMMAND)
                .about("formats to an URL QR-Code")
                .arg(Arg::with_name("url").required(true).value_name("URL")),
        )
        .subcommand(
            SubCommand::with_name(PHONE_COMMAND)
                .about("formats to a phone number QR-Code")
                .arg(Arg::with_name("number").required(true).value_name("NUMBER")),
        )
        .subcommand(
            SubCommand::with_name(SKYPE_COMMAND)
                .about("formats to a skype call QR-Code")
                .arg(Arg::with_name("name").required(true).value_name("HANDLE")),
        )
        .subcommand(
            SubCommand::with_name(WHATSAPP_COMMAND)
                .about("formats to a whatsapp message QR-Code")
                .arg(Arg::with_name("message").required(true).value_name(
                    "MESSAGE",
                )),
        )
        .subcommand(
            SubCommand::with_name(SMS_COMMAND)
                .about("formats to a sms message QR-Code")
                .arg(Arg::with_name("number").required(true))
                .arg(Arg::with_name("subject").default_value(""))
                .arg(
                    Arg::with_name("encoding")
                        .possible_values(&["SMS", "SMSTO", "SMS_iOS"])
                        .default_value("SMS"),
                ),
        )
        .subcommand(
            SubCommand::with_name(MMS_COMMAND)
                .about("formats to a mms message QR-Code")
                .arg(Arg::with_name("number").required(true))
                .arg(Arg::with_name("subject").default_value(""))
                .arg(
                    Arg::with_name("encoding")
                        .possible_values(&["MMS", "MMSTO"])
                        .default_value("MMS"),
                ),
        )
        .subcommand(
            SubCommand::with_name(GEO_COMMAND)
                .about("formats to a geospacial location QR-Code")
                .arg(Arg::with_name("latitude").required(true))
                .arg(Arg::with_name("longitude").required(true))
                .arg(
                    Arg::with_name("encoding")
                        .possible_values(&["GEO", "GoogleMaps"])
                        .default_value("GEO"),
                ),
        )
        .subcommand(
            SubCommand::with_name(BOOKMARK_COMMAND)
                .about("formats to a bookmark QR-Code")
                .arg(Arg::with_name("title").required(true))
                .arg(Arg::with_name("url").required(true)),
        )
        .subcommand(
            SubCommand::with_name(BITCOIN_COMMAND)
                .about("outputs a bitcoin adress/transaction")
                .arg(Arg::with_name("address").required(true))
                .arg(Arg::with_name("amount"))
                .arg(Arg::with_name("label"))
                .arg(Arg::with_name("message")),
        )
}
<|endoftext|>


<commit_before>extern crate hostname;
extern crate glob;
extern crate rust_shell as shell;

use std::io;
use std::io::Write;
use std::str;
use hostname::get_hostname;
use shell::state::ShellState;
use shell::circular_buffer::CircularBuffer;
use shell::envars::Envars;

fn main() {
    // TODO: Factor out more helper functions
    // TODO: Semicolons between commands on a single line
    // TODO: Pipes and output redirection
    // TODO: Syntax highlighting
    let mut shell = ShellState {
        variables: Envars::load(),
        history: CircularBuffer::new(10000),
    };

    // login only gives me $HOME, $SHELL, $PATH, $LOGNAME, and $MAIL, so provide defaults here
    shell.variables.insert("PROMPT", "PROMPT={BOLD}{WHITE}╭{RED} ➜ {GREEN}{$USER}@{$HOSTNAME}:{CYAN}{$PWD}{WHITE}\n╰ ➤ ");
    shell.variables.insert("HISTSIZE", "1000");
    shell.variables.insert("USER", "ben");
    shell.variables.insert("HOSTNAME", get_hostname().unwrap().as_ref());

    shell.read_config();

    shell.load_history();

    let mut input_buffer = String::new();
    loop {
        shell.prompt_read(&mut input_buffer);
        let mut args = input_buffer.split_whitespace();
        let cmd = args.next().unwrap_or("").to_owned();

        match cmd.as_ref() {
            "" => print!(""),
            "cd" => shell.cd(args),
            "echo" => shell.echo(args),
            "exit" => {
                io::stdout().flush().unwrap();
                shell.save_history();
                std::process::exit(0)
            },
            "history" => shell.print_history(),
            _ => shell.run_command(&cmd, args)
        };
    }
}
<commit_msg>fixed changes to initial config so this works as a login shell<commit_after>extern crate hostname;
extern crate glob;
extern crate rust_shell as shell;

use std::io;
use std::io::Write;
use std::str;
use hostname::get_hostname;
use shell::state::ShellState;
use shell::circular_buffer::CircularBuffer;
use shell::envars::Envars;

fn main() {
    // TODO: Factor out more helper functions
    // TODO: Semicolons between commands on a single line
    // TODO: Pipes and output redirection
    // TODO: Syntax highlighting
    let mut shell = ShellState {
        variables: Envars::load(),
        history: CircularBuffer::new(10000),
    };

    // login only gives me $HOME, $SHELL, $PATH, $LOGNAME, and $MAIL, so provide defaults here
    shell.variables.insert("PROMPT", "{BOLD}{WHITE}╭{RED} ➜ {GREEN}{$USER}@{$HOSTNAME}:{CYAN}{$PWD}{WHITE}\n╰ ➤ ");
    shell.variables.insert("HISTSIZE", "1000");
    shell.variables.insert("HOSTNAME", get_hostname().unwrap().as_ref());
    let username = shell.variables.get("LOGNAME").unwrap().clone();
    shell.variables.insert("USER", username.to_string_lossy().as_ref());
    let home = shell.variables.get("HOME").unwrap().clone();
    shell.variables.insert("PWD", home.to_string_lossy().as_ref());

    shell.read_config();

    shell.load_history();

    let mut input_buffer = String::new();
    loop {
        shell.prompt_read(&mut input_buffer);
        let mut args = input_buffer.split_whitespace();
        let cmd = args.next().unwrap_or("").to_owned();

        match cmd.as_ref() {
            "" => print!(""),
            "cd" => shell.cd(args),
            "echo" => shell.echo(args),
            "exit" => {
                io::stdout().flush().unwrap();
                shell.save_history();
                std::process::exit(0)
            },
            "history" => shell.print_history(),
            _ => shell.run_command(&cmd, args)
        };
    }
}
<|endoftext|>