ice_core 0.5.1

High performance Web engine
use std::cell::RefCell;

use slab::Slab;
use std::fs::{File, OpenOptions};

use super::super::namespace::InvokeContext;
use super::super::error::ErrorCode;
use wasm_core::value::Value;
use config::AppPermission;

decl_namespace!(
    FileNs,
    "file",
    FileImpl,
    open,
    close,
    read,
    write,
    flush,
    seek
);

pub struct FileImpl {
    handles: RefCell<Slab<File>>
}

impl FileImpl {
    pub fn new() -> FileImpl {
        FileImpl {
            handles: RefCell::new(Slab::new())
        }
    }

    pub fn open(&self, ctx: InvokeContext) -> Option<Value> {
        let path = ctx.extract_str(0, 1);
        let mode = ctx.extract_str(2, 3);
        let mut opt = OpenOptions::new();

        let app = ctx.app.upgrade().unwrap();
        match app.check_permission(&AppPermission::FileOpenReadOnlyAny)
            .or_else(|_| app.check_permission(&AppPermission::FileOpenReadWriteAny)) {
                Ok(_) => {},
                Err(_) => {
                    derror!(
                        logger!(&app.name),
                        "FileOpenReadOnlyAny or FileOpenReadWriteAny permissions are required"
                    );
                    return Some(ErrorCode::PermissionDenied.to_ret());
                }
            }

        let mut need_write = false;

        for ch in mode.chars() {
            match ch {
                'r' => {
                    opt.read(true);
                },
                'w' => {
                    opt.write(true);
                    need_write = true;
                },
                'a' => {
                    opt.append(true);
                    need_write = true;
                },
                't' => {
                    opt.truncate(true);
                    need_write = true;
                },
                'c' => {
                    opt.create(true);
                    need_write = true;
                },
                'n' => {
                    opt.create_new(true);
                    need_write = true;
                },
                _ => return Some(ErrorCode::InvalidInput.to_ret())
            }
        }

        if need_write {
            match app.check_permission(&AppPermission::FileOpenReadWriteAny) {
                Ok(_) => {},
                Err(_) => {
                    derror!(
                        logger!(&app.name),
                        "FileOpenReadWriteAny permission is required"
                    );
                    return Some(ErrorCode::PermissionDenied.to_ret());
                }
            }
        }

        let f = match opt.open(path) {
            Ok(v) => v,
            Err(e) => return Some(ErrorCode::from(e.kind()).to_ret())
        };

        let id = self.handles.borrow_mut().insert(f);

        Some(Value::I32(id as i32))
    }

    pub fn close(&self, ctx: InvokeContext) -> Option<Value> {
        let id = ctx.args[0].get_i32().unwrap() as usize;
        self.handles.borrow_mut().remove(id);
        None
    }

    pub fn read(&self, mut ctx: InvokeContext) -> Option<Value> {
        use std::io::Read;

        let id = ctx.args[0].get_i32().unwrap() as usize;
        let buf = ctx.extract_bytes_mut(1, 2);

        let mut handles = self.handles.borrow_mut();
        let file = handles.get_mut(id).unwrap();

        Some(match file.read(buf) {
            Ok(n) => Value::I32(n as i32),
            Err(e) => ErrorCode::from(e.kind()).to_ret()
        })
    }

    pub fn write(&self, ctx: InvokeContext) -> Option<Value> {
        use std::io::Write;

        let id = ctx.args[0].get_i32().unwrap() as usize;
        let buf = ctx.extract_bytes(1, 2);

        let mut handles = self.handles.borrow_mut();
        let file = handles.get_mut(id).unwrap();

        Some(match file.write(buf) {
            Ok(n) => Value::I32(n as i32),
            Err(e) => ErrorCode::from(e.kind()).to_ret()
        })
    }

    pub fn flush(&self, ctx: InvokeContext) -> Option<Value> {
        use std::io::Write;

        let id = ctx.args[0].get_i32().unwrap() as usize;

        let mut handles = self.handles.borrow_mut();
        let file = handles.get_mut(id).unwrap();

        Some(match file.flush() {
            Ok(()) => ErrorCode::Success.to_ret(),
            Err(e) => ErrorCode::from(e.kind()).to_ret()
        })
    }

    pub fn seek(&self, ctx: InvokeContext) -> Option<Value> {
        use std::io::{Seek, SeekFrom};

        let id = ctx.args[0].get_i32().unwrap() as usize;
        let from = ctx.args[1].get_i32().unwrap();
        let offset = ctx.args[2].get_i64().unwrap();

        let from = match from {
            0 => SeekFrom::Start(offset as u64),
            1 => SeekFrom::End(offset),
            2 => SeekFrom::Current(offset),
            _ => return Some(
                Value::I64(ErrorCode::InvalidInput.to_i32() as i64)
            )
        };

        let mut handles = self.handles.borrow_mut();
        let file = handles.get_mut(id).unwrap();

        Some(Value::I64(match file.seek(from) {
            Ok(v) => v as i64,
            Err(e) => ErrorCode::from(e.kind()).to_i32() as i64
        }))
    }
}