wasi-c2: rewrite wasi-common in terms of system-interface

This commit is contained in:
Pat Hickey
2020-11-17 15:23:26 -08:00
parent efe7f37542
commit b87908de9b
11 changed files with 801 additions and 1 deletions

49
Cargo.lock generated
View File

@@ -1458,6 +1458,19 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"
[[package]]
name = "posish"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a81e5017f1c873447782cd776e6ec307af670ecad29e934042005a0dec6864dd"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"errno",
"itoa",
"libc",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
@@ -1992,6 +2005,16 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "system-interface"
version = "0.0.1-alpha.0"
dependencies = [
"atty",
"posish",
"winapi",
"winx 0.20.0",
]
[[package]]
name = "target-lexicon"
version = "0.11.1"
@@ -2283,6 +2306,19 @@ version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi-c2"
version = "0.21.0"
dependencies = [
"anyhow",
"getrandom 0.2.0",
"libc",
"system-interface",
"thiserror",
"tracing",
"wiggle",
]
[[package]]
name = "wasi-common"
version = "0.21.0"
@@ -2298,7 +2334,7 @@ dependencies = [
"tracing",
"wiggle",
"winapi",
"winx",
"winx 0.21.0",
"yanix",
]
@@ -2810,6 +2846,17 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winx"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b25e4ae373f2f2f7f5f187974ed315719ce74160859027c80deb1f68b3c0c966"
dependencies = [
"bitflags",
"cvt",
"winapi",
]
[[package]]
name = "winx"
version = "0.21.0"

View File

@@ -70,6 +70,7 @@ members = [
"crates/misc/rust",
"crates/wiggle",
"crates/wiggle/wasmtime",
"crates/wasi-c2",
"examples/fib-debug/wasm",
"examples/wasi/wasm",
"examples/wasi-fs/wasm",

35
crates/wasi-c2/Cargo.toml Normal file
View File

@@ -0,0 +1,35 @@
[package]
name = "wasi-c2"
version = "0.21.0"
authors = ["The Wasmtime Project Developers"]
description = "WASI implementation in Rust"
license = "Apache-2.0 WITH LLVM-exception"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition = "2018"
include = ["src/**/*", "LICENSE", "build.rs"]
build = "build.rs"
[dependencies]
anyhow = "1.0"
thiserror = "1.0"
libc = "0.2"
getrandom = { version = "0.2.0", features = ["std"] }
wiggle = { path = "../wiggle", default-features = false, version = "0.21.0" }
tracing = "0.1.19"
system-interface = { path = "../../../system-interface" }
[badges]
maintenance = { status = "actively-developed" }
[features]
default = ["trace_log"]
# This feature enables the `tracing` logs in the calls to target the `log`
# ecosystem of backends (e.g. `env_logger`. Disable this if you want to use
# `tracing-subscriber`.
trace_log = [ "wiggle/tracing_log", "tracing/log" ]
# Need to make the wiggle_metadata feature available to consumers of this
# crate if they want the snapshots to have metadata available.
wiggle_metadata = ["wiggle/wiggle_metadata"]

8
crates/wasi-c2/build.rs Normal file
View File

@@ -0,0 +1,8 @@
// Tell any dependencies, if necessary, where our WASI submodule is so they can
// use the same witx files if they want.
fn main() {
let cwd = std::env::current_dir().unwrap();
let wasi = cwd.join("..").join("wasi-common").join("WASI");
println!("cargo:wasi={}", wasi.display());
println!("cargo:rustc-env=WASI_ROOT={}", wasi.display());
}

26
crates/wasi-c2/src/ctx.rs Normal file
View File

@@ -0,0 +1,26 @@
use crate::table::Table;
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
pub struct WasiCtx {
table: Rc<RefCell<Table>>,
}
impl WasiCtx {
pub fn new() -> Self {
WasiCtx {
table: Rc::new(RefCell::new(Table::new())),
}
}
pub fn table(&self) -> RefMut<Table> {
self.table.borrow_mut()
}
}
pub trait WasiDir {}
pub(crate) struct DirEntry {
pub(crate) flags: u32,
pub(crate) dir: Box<dyn WasiDir>,
}

151
crates/wasi-c2/src/error.rs Normal file
View File

@@ -0,0 +1,151 @@
use crate::file::FileCaps;
use thiserror::Error;
/// Internal error type for the `wasi-common` crate.
/// Contains variants of the WASI `$errno` type are added according to what is actually used internally by
/// the crate. Not all values are represented presently.
#[derive(Debug, Error)]
pub enum Error {
#[error("Wiggle GuestError: {0}")]
Guest(#[from] wiggle::GuestError),
#[error("TryFromIntError: {0}")]
TryFromInt(#[from] std::num::TryFromIntError),
#[error("Utf8Error: {0}")]
Utf8(#[from] std::str::Utf8Error),
#[error("GetRandom: {0}")]
GetRandom(#[from] getrandom::Error),
/// Errno::Notcapable: Extension: Capabilities insufficient
#[error("File not capable: {0}")]
FileNotCapable(FileCaps),
/// The host OS may return an io error that doesn't match one of the
/// wasi errno variants we expect. We do not expose the details of this
/// error to the user.
#[error("Unexpected IoError: {0}")]
UnexpectedIo(#[source] std::io::Error),
// Below this, all variants are from the `$errno` type:
/// Errno::TooBig: Argument list too long
#[error("TooBig: Argument list too long")]
TooBig,
/// Errno::Acces: Permission denied
#[error("Acces: Permission denied")]
Acces,
/// Errno::Badf: Bad file descriptor
#[error("Badf: Bad file descriptor")]
Badf,
/// Errno::Busy: Device or resource busy
#[error("Busy: Device or resource busy")]
Busy,
/// Errno::Exist: File exists
#[error("Exist: File exists")]
Exist,
/// Errno::Fault: Bad address
#[error("Fault: Bad address")]
Fault,
/// Errno::Fbig: File too large
#[error("Fbig: File too large")]
Fbig,
/// Errno::Ilseq: Illegal byte sequence
#[error("Ilseq: Illegal byte sequence")]
Ilseq,
/// Errno::Inval: Invalid argument
#[error("Inval: Invalid argument")]
Inval,
/// Errno::Io: I/O error
#[error("Io: I/o error")]
Io,
/// Errno::Isdir: Is a directory
#[error("Isdir: Is a directory")]
Isdir,
/// Errno::Loop: Too many levels of symbolic links
#[error("Loop: Too many levels of symbolic links")]
Loop,
/// Errno::Mfile: File descriptor value too large
#[error("Mfile: File descriptor value too large")]
Mfile,
/// Errno::Mlink: Too many links
#[error("Mlink: Too many links")]
Mlink,
/// Errno::Nametoolong: Filename too long
#[error("Nametoolong: Filename too long")]
Nametoolong,
/// Errno::Nfile: Too many files open in system
#[error("Nfile: Too many files open in system")]
Nfile,
/// Errno::Noent: No such file or directory
#[error("Noent: No such file or directory")]
Noent,
/// Errno::Nomem: Not enough space
#[error("Nomem: Not enough space")]
Nomem,
/// Errno::Nospc: No space left on device
#[error("Nospc: No space left on device")]
Nospc,
/// Errno::Notdir: Not a directory or a symbolic link to a directory.
#[error("Notdir: Not a directory or a symbolic link to a directory")]
Notdir,
/// Errno::Notempty: Directory not empty.
#[error("Notempty: Directory not empty")]
Notempty,
/// Errno::Notsup: Not supported, or operation not supported on socket.
#[error("Notsup: Not supported, or operation not supported on socket")]
Notsup,
/// Errno::Overflow: Value too large to be stored in data type.
#[error("Overflow: Value too large to be stored in data type")]
Overflow,
/// Errno::Pipe: Broken pipe
#[error("Pipe: Broken pipe")]
Pipe,
/// Errno::Perm: Operation not permitted
#[error("Perm: Operation not permitted")]
Perm,
/// Errno::Spipe: Invalid seek
#[error("Spipe: Invalid seek")]
Spipe,
}
impl From<std::convert::Infallible> for Error {
fn from(_err: std::convert::Infallible) -> Self {
unreachable!("should be impossible: From<Infallible>")
}
}
use std::io;
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
match err.raw_os_error() {
Some(code) => match code {
libc::EPIPE => Self::Pipe,
libc::EPERM => Self::Perm,
libc::ENOENT => Self::Noent,
libc::ENOMEM => Self::Nomem,
libc::E2BIG => Self::TooBig,
libc::EIO => Self::Io,
libc::EBADF => Self::Badf,
libc::EBUSY => Self::Busy,
libc::EACCES => Self::Acces,
libc::EFAULT => Self::Fault,
libc::ENOTDIR => Self::Notdir,
libc::EISDIR => Self::Isdir,
libc::EINVAL => Self::Inval,
libc::EEXIST => Self::Exist,
libc::EFBIG => Self::Fbig,
libc::ENOSPC => Self::Nospc,
libc::ESPIPE => Self::Spipe,
libc::EMFILE => Self::Mfile,
libc::EMLINK => Self::Mlink,
libc::ENAMETOOLONG => Self::Nametoolong,
libc::ENFILE => Self::Nfile,
libc::ENOTEMPTY => Self::Notempty,
libc::ELOOP => Self::Loop,
libc::EOVERFLOW => Self::Overflow,
libc::EILSEQ => Self::Ilseq,
libc::ENOTSUP => Self::Notsup,
_ => Self::UnexpectedIo(err),
},
None => Self::UnexpectedIo(err),
}
}
}

View File

@@ -0,0 +1,53 @@
use crate::Error;
use std::ops::Deref;
use system_interface::fs::FileIoExt;
pub trait WasiFile: FileIoExt {}
pub(crate) struct FileEntry {
pub(crate) base_caps: FileCaps,
pub(crate) inheriting_caps: FileCaps,
pub(crate) file: Box<dyn WasiFile>,
}
impl FileEntry {
pub fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> {
if self.base_caps.contains(&caps) && self.inheriting_caps.contains(&caps) {
Ok(self.file.deref())
} else {
Err(Error::FileNotCapable(caps))
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct FileCaps {
flags: u32,
}
impl FileCaps {
pub fn empty() -> Self {
FileCaps { flags: 0 }
}
/// Checks if `other` is a subset of those capabilties:
pub fn contains(&self, other: &Self) -> bool {
self.flags & other.flags == other.flags
}
pub const DATASYNC: Self = FileCaps { flags: 1 };
pub const READ: Self = FileCaps { flags: 2 };
pub const SEEK: Self = FileCaps { flags: 4 };
pub const FDSTAT_SET_FLAGS: Self = FileCaps { flags: 8 };
pub const SYNC: Self = FileCaps { flags: 16 };
pub const TELL: Self = FileCaps { flags: 32 };
pub const WRITE: Self = FileCaps { flags: 64 };
pub const ADVISE: Self = FileCaps { flags: 128 };
pub const ALLOCATE: Self = FileCaps { flags: 256 };
}
impl std::fmt::Display for FileCaps {
fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
todo!()
}
}

View File

@@ -0,0 +1,8 @@
mod ctx;
mod error;
mod file;
pub mod snapshots;
pub mod table;
pub use ctx::WasiCtx;
pub use error::Error;

View File

@@ -0,0 +1 @@
pub mod preview_1;

View File

@@ -0,0 +1,424 @@
#![allow(unused_variables)]
use crate::file::{FileCaps, FileEntry};
use crate::{Error, WasiCtx};
use std::cell::RefMut;
use tracing::debug;
use wiggle::GuestPtr;
wiggle::from_witx!({
witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"],
ctx: WasiCtx,
errors: { errno => Error },
});
impl wiggle::GuestErrorType for types::Errno {
fn success() -> Self {
Self::Success
}
}
impl types::GuestErrorConversion for WasiCtx {
fn into_errno(&self, e: wiggle::GuestError) -> types::Errno {
debug!("Guest error: {:?}", e);
e.into()
}
}
impl types::UserErrorConversion for WasiCtx {
fn errno_from_error(&self, e: Error) -> types::Errno {
debug!("Error: {:?}", e);
e.into()
}
}
impl From<Error> for types::Errno {
fn from(e: Error) -> types::Errno {
use types::Errno;
match e {
Error::Guest(e) => e.into(),
Error::TryFromInt(_) => Errno::Overflow,
Error::Utf8(_) => Errno::Ilseq,
Error::UnexpectedIo(_) => Errno::Io,
Error::GetRandom(_) => Errno::Io,
Error::TooBig => Errno::TooBig,
Error::Acces => Errno::Acces,
Error::Badf => Errno::Badf,
Error::Busy => Errno::Busy,
Error::Exist => Errno::Exist,
Error::Fault => Errno::Fault,
Error::Fbig => Errno::Fbig,
Error::Ilseq => Errno::Ilseq,
Error::Inval => Errno::Inval,
Error::Io => Errno::Io,
Error::Isdir => Errno::Isdir,
Error::Loop => Errno::Loop,
Error::Mfile => Errno::Mfile,
Error::Mlink => Errno::Mlink,
Error::Nametoolong => Errno::Nametoolong,
Error::Nfile => Errno::Nfile,
Error::Noent => Errno::Noent,
Error::Nomem => Errno::Nomem,
Error::Nospc => Errno::Nospc,
Error::Notdir => Errno::Notdir,
Error::Notempty => Errno::Notempty,
Error::Notsup => Errno::Notsup,
Error::Overflow => Errno::Overflow,
Error::Pipe => Errno::Pipe,
Error::Perm => Errno::Perm,
Error::Spipe => Errno::Spipe,
Error::FileNotCapable { .. } => Errno::Notcapable,
}
}
}
impl From<wiggle::GuestError> for types::Errno {
fn from(err: wiggle::GuestError) -> Self {
use wiggle::GuestError::*;
match err {
InvalidFlagValue { .. } => Self::Inval,
InvalidEnumValue { .. } => Self::Inval,
PtrOverflow { .. } => Self::Fault,
PtrOutOfBounds { .. } => Self::Fault,
PtrNotAligned { .. } => Self::Inval,
PtrBorrowed { .. } => Self::Fault,
InvalidUtf8 { .. } => Self::Ilseq,
TryFromIntError { .. } => Self::Overflow,
InFunc { .. } => Self::Inval,
InDataField { .. } => Self::Inval,
SliceLengthsDiffer { .. } => Self::Fault,
BorrowCheckerOutOfHandles { .. } => Self::Fault,
}
}
}
impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
fn args_get<'b>(
&self,
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
argv_buf: &GuestPtr<'b, u8>,
) -> Result<(), Error> {
unimplemented!()
}
fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
unimplemented!()
}
fn environ_get<'b>(
&self,
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
environ_buf: &GuestPtr<'b, u8>,
) -> Result<(), Error> {
unimplemented!()
}
fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
unimplemented!()
}
fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
unimplemented!()
}
fn clock_time_get(
&self,
id: types::Clockid,
_precision: types::Timestamp,
) -> Result<types::Timestamp, Error> {
unimplemented!()
}
fn fd_advise(
&self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
advice: types::Advice,
) -> Result<(), Error> {
let table = self.table();
let file_entry: RefMut<FileEntry> = table.get(u32::from(fd))?;
let f = file_entry.get_cap(FileCaps::ADVISE)?;
f.advise(offset, len, advice.into())?;
Ok(())
}
fn fd_allocate(
&self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
) -> Result<(), Error> {
unimplemented!()
}
fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
unimplemented!()
}
fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
unimplemented!()
}
fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
unimplemented!()
}
fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
unimplemented!()
}
fn fd_fdstat_set_rights(
&self,
fd: types::Fd,
fs_rights_base: types::Rights,
fs_rights_inheriting: types::Rights,
) -> Result<(), Error> {
unimplemented!()
}
fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
unimplemented!()
}
fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<(), Error> {
unimplemented!()
}
fn fd_filestat_set_times(
&self,
fd: types::Fd,
atim: types::Timestamp,
mtim: types::Timestamp,
fst_flags: types::Fstflags,
) -> Result<(), Error> {
unimplemented!()
}
fn fd_read(&self, fd: types::Fd, iovs: &types::IovecArray<'_>) -> Result<types::Size, Error> {
unimplemented!()
}
fn fd_pread(
&self,
fd: types::Fd,
iovs: &types::IovecArray<'_>,
offset: types::Filesize,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn fd_write(
&self,
fd: types::Fd,
ciovs: &types::CiovecArray<'_>,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn fd_pwrite(
&self,
fd: types::Fd,
ciovs: &types::CiovecArray<'_>,
offset: types::Filesize,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
unimplemented!()
}
fn fd_prestat_dir_name(
&self,
fd: types::Fd,
path: &GuestPtr<u8>,
path_len: types::Size,
) -> Result<(), Error> {
unimplemented!()
}
fn fd_readdir(
&self,
fd: types::Fd,
buf: &GuestPtr<u8>,
buf_len: types::Size,
cookie: types::Dircookie,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
unimplemented!()
}
fn fd_seek(
&self,
fd: types::Fd,
offset: types::Filedelta,
whence: types::Whence,
) -> Result<types::Filesize, Error> {
unimplemented!()
}
fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
unimplemented!()
}
fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
unimplemented!()
}
fn path_create_directory(
&self,
dirfd: types::Fd,
path: &GuestPtr<'_, str>,
) -> Result<(), Error> {
unimplemented!()
}
fn path_filestat_get(
&self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'_, str>,
) -> Result<types::Filestat, Error> {
unimplemented!()
}
fn path_filestat_set_times(
&self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'_, str>,
atim: types::Timestamp,
mtim: types::Timestamp,
fst_flags: types::Fstflags,
) -> Result<(), Error> {
unimplemented!()
}
fn path_link(
&self,
old_fd: types::Fd,
old_flags: types::Lookupflags,
old_path: &GuestPtr<'_, str>,
new_fd: types::Fd,
new_path: &GuestPtr<'_, str>,
) -> Result<(), Error> {
unimplemented!()
}
fn path_open(
&self,
dirfd: types::Fd,
dirflags: types::Lookupflags,
path: &GuestPtr<'_, str>,
oflags: types::Oflags,
fs_rights_base: types::Rights,
fs_rights_inheriting: types::Rights,
fdflags: types::Fdflags,
) -> Result<types::Fd, Error> {
unimplemented!()
}
fn path_readlink(
&self,
dirfd: types::Fd,
path: &GuestPtr<'_, str>,
buf: &GuestPtr<u8>,
buf_len: types::Size,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn path_remove_directory(
&self,
dirfd: types::Fd,
path: &GuestPtr<'_, str>,
) -> Result<(), Error> {
unimplemented!()
}
fn path_rename(
&self,
old_fd: types::Fd,
old_path: &GuestPtr<'_, str>,
new_fd: types::Fd,
new_path: &GuestPtr<'_, str>,
) -> Result<(), Error> {
unimplemented!()
}
fn path_symlink(
&self,
old_path: &GuestPtr<'_, str>,
dirfd: types::Fd,
new_path: &GuestPtr<'_, str>,
) -> Result<(), Error> {
unimplemented!()
}
fn path_unlink_file(&self, dirfd: types::Fd, path: &GuestPtr<'_, str>) -> Result<(), Error> {
unimplemented!()
}
fn poll_oneoff(
&self,
subs: &GuestPtr<types::Subscription>,
events: &GuestPtr<types::Event>,
nsubscriptions: types::Size,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn proc_exit(&self, _rval: types::Exitcode) -> Result<(), ()> {
unimplemented!()
}
fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
unimplemented!()
}
fn sched_yield(&self) -> Result<(), Error> {
unimplemented!()
}
fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<(), Error> {
unimplemented!()
}
fn sock_recv(
&self,
_fd: types::Fd,
_ri_data: &types::IovecArray<'_>,
_ri_flags: types::Riflags,
) -> Result<(types::Size, types::Roflags), Error> {
unimplemented!()
}
fn sock_send(
&self,
_fd: types::Fd,
_si_data: &types::CiovecArray<'_>,
_si_flags: types::Siflags,
) -> Result<types::Size, Error> {
unimplemented!()
}
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
unimplemented!()
}
}
impl From<types::Advice> for system_interface::fs::Advice {
fn from(advice: types::Advice) -> system_interface::fs::Advice {
match advice {
types::Advice::Normal => system_interface::fs::Advice::Normal,
types::Advice::Sequential => system_interface::fs::Advice::Sequential,
types::Advice::Random => system_interface::fs::Advice::Random,
types::Advice::Willneed => system_interface::fs::Advice::WillNeed,
types::Advice::Dontneed => system_interface::fs::Advice::DontNeed,
types::Advice::Noreuse => system_interface::fs::Advice::NoReuse,
}
}
}

View File

@@ -0,0 +1,46 @@
use crate::Error;
use std::any::Any;
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
pub struct Table {
map: HashMap<u32, RefCell<Box<dyn Any>>>,
next_key: u32,
}
impl Table {
pub fn new() -> Self {
Table {
map: HashMap::new(),
next_key: 0,
}
}
pub fn insert(&mut self, a: impl Any + Sized) -> u32 {
let key = self.next_key;
self.next_key += 1;
self.map.insert(key, RefCell::new(Box::new(a)));
key
}
// Todo: we can refine these errors and translate them to Exist at abi
pub fn get<T: Any + Sized>(&self, key: u32) -> Result<RefMut<T>, Error> {
if let Some(refcell) = self.map.get(&key) {
if let Ok(refmut) = refcell.try_borrow_mut() {
if refmut.is::<T>() {
Ok(RefMut::map(refmut, |r| r.downcast_mut::<T>().unwrap()))
} else {
Err(Error::Exist) // Exists at another type
}
} else {
Err(Error::Exist) // Does exist, but borrowed
}
} else {
Err(Error::Exist) // Does not exist
}
}
pub fn delete(&mut self, key: u32) -> Option<Box<dyn Any>> {
self.map.remove(&key).map(|rc| RefCell::into_inner(rc))
}
}