use crate::error::Error;
use crate::helpers::{max_usize, Ignore};
use alloc::vec::Vec;
use minicbor::{Decode, Encode};
use oc_wasm_futures::invoke::{component_method, Buffer};
use oc_wasm_helpers::Lockable;
use oc_wasm_safe::{
component::{Invoker, MethodCallError},
descriptor, extref, Address,
};
pub const TYPE: &str = "filesystem";
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Filesystem(Address);
impl Filesystem {
#[must_use = "This function is only useful for its return value"]
pub fn new(address: Address) -> Self {
Self(address)
}
#[must_use = "This function is only useful for its return value"]
pub fn address(&self) -> &Address {
&self.0
}
}
impl<'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B> for Filesystem {
type Locked = Locked<'invoker, 'buffer, B>;
fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
Locked {
address: self.0,
invoker,
buffer,
}
}
}
pub struct Locked<'invoker, 'buffer, B: Buffer> {
address: Address,
invoker: &'invoker mut Invoker,
buffer: &'buffer mut B,
}
impl<'invoker, 'buffer, B: Buffer> Locked<'invoker, 'buffer, B> {
pub async fn get_label(self) -> Result<Option<&'buffer str>, Error> {
let ret: (Option<&'buffer str>,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getLabel",
None,
)
.await?;
Ok(ret.0)
}
pub async fn set_label(self, label: Option<&str>) -> Result<Option<&'buffer str>, Error> {
let label = label.map(|l| unsafe { extref::String::new(l) });
let ret: Result<(Option<&'buffer str>,), MethodCallError<'_>> = component_method(
self.invoker,
self.buffer,
&self.address,
"setLabel",
Some(&(label,)),
)
.await;
match ret {
Ok((label,)) => Ok(label),
Err(MethodCallError::BadParameters(_)) => Err(Error::StorageReadOnly),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(MethodCallError::Other(_)) => Err(Error::Unsupported),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn is_read_only(&mut self) -> Result<bool, Error> {
let ret: (bool,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"isReadOnly",
None,
)
.await?;
Ok(ret.0)
}
pub async fn get_space_total(&mut self) -> Result<u64, Error> {
enum Return {
Finite(u64),
Infinite,
}
use minicbor::{data::Type, Decoder};
impl<Context> Decode<'_, Context> for Return {
fn decode(
d: &mut Decoder<'_>,
_: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
match d.datatype()? {
Type::F16 | Type::F32 | Type::F64 => {
d.skip()?;
Ok(Return::Infinite)
}
_ => Ok(Return::Finite(d.u64()?)),
}
}
}
let ret: (Return,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"spaceTotal",
None,
)
.await?;
Ok(match ret.0 {
Return::Finite(x) => x,
Return::Infinite => u64::MAX,
})
}
pub async fn get_space_used(&mut self) -> Result<u64, Error> {
let ret: (u64,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"spaceUsed",
None,
)
.await?;
Ok(ret.0)
}
pub async fn exists(&mut self, path: &str) -> Result<bool, Error> {
self.call_path_to_value("exists", path).await
}
pub async fn size(&mut self, path: &str) -> Result<u64, Error> {
self.call_path_to_value("size", path).await
}
pub async fn is_directory(&mut self, path: &str) -> Result<bool, Error> {
self.call_path_to_value("isDirectory", path).await
}
pub async fn last_modified(&mut self, path: &str) -> Result<u64, Error> {
self.call_path_to_value("lastModified", path).await
}
pub async fn list(self, path: &str) -> Result<Vec<DirectoryEntry<'buffer>>, Error> {
let path = unsafe { extref::String::new(path) };
let ret: Result<(Option<Vec<DirectoryEntry<'buffer>>>,), _> = component_method(
self.invoker,
self.buffer,
&self.address,
"list",
Some(&(path,)),
)
.await;
match ret {
Ok((Some(listing),)) => Ok(listing),
Ok((None,)) | Err(MethodCallError::Other(_)) => Err(Error::FileNotFound),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn make_directory(&mut self, path: &str) -> Result<(), Error> {
let ret: bool = self.call_path_to_value("makeDirectory", path).await?;
if ret {
Ok(())
} else {
Err(Error::Failed)
}
}
pub async fn remove(&mut self, path: &str) -> Result<(), Error> {
let removed: bool = self.call_path_to_value("remove", path).await?;
if removed {
Ok(())
} else {
Err(Error::Failed)
}
}
pub async fn rename(&mut self, source: &str, destination: &str) -> Result<(), Error> {
let source = unsafe { extref::String::new(source) };
let destination = unsafe { extref::String::new(destination) };
let ret: Result<(bool,), _> = component_method(
self.invoker,
self.buffer,
&self.address,
"rename",
Some(&(source, destination)),
)
.await;
match ret {
Ok((true,)) => Ok(()),
Ok((false,)) => Err(Error::Failed),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(MethodCallError::Other(_)) => Err(Error::BadFilename),
Err(e) => Err(e.into()),
}
}
pub async fn open_read(&mut self, path: &str) -> Result<ReadHandle, Error> {
Ok(ReadHandle {
address: self.address,
descriptor: self.call_open(path, "r", Error::FileNotFound).await?,
})
}
pub async fn open_write(&mut self, path: &str, mode: WriteMode) -> Result<WriteHandle, Error> {
Ok(WriteHandle {
address: self.address,
descriptor: self.call_open(path, mode.as_str(), Error::Failed).await?,
})
}
async fn call_path_to_value<T>(&mut self, method: &str, path: &str) -> Result<T, Error>
where
for<'a> T: Decode<'a, ()>,
{
let path = unsafe { extref::String::new(path) };
let ret: Result<(T,), _> = component_method(
self.invoker,
self.buffer,
&self.address,
method,
Some(&(path,)),
)
.await;
match ret {
Ok((v,)) => Ok(v),
Err(MethodCallError::Other(_)) => Err(Error::BadFilename),
Err(e) => Err(e.into()),
}
}
async fn call_open(
&mut self,
path: &str,
mode: &str,
file_not_found_error: Error,
) -> Result<descriptor::Owned, Error> {
let path = unsafe { extref::String::new(path) };
let descriptor: Result<(descriptor::Decoded,), MethodCallError<'_>> = component_method(
self.invoker,
self.buffer,
&self.address,
"open",
Some(&(path, mode)),
)
.await;
match descriptor {
Ok((descriptor,)) => Ok(
unsafe { descriptor.into_owned() },
),
Err(MethodCallError::Other(exception)) => {
if exception.is_type("java.io.FileNotFoundException") {
Err(file_not_found_error)
} else if exception.is_type("java.io.IOException") {
const TOO_MANY_OPEN_HANDLES: &str = "too many open handles";
const ERROR_MESSAGE_BUFFER_SIZE: usize = TOO_MANY_OPEN_HANDLES.len();
let mut message_buffer = [0_u8; ERROR_MESSAGE_BUFFER_SIZE];
match exception.message(&mut message_buffer) {
Ok(TOO_MANY_OPEN_HANDLES) => Err(Error::TooManyDescriptors),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
} else {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
Err(e) => Err(e.into()),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum DirectoryEntryType {
File,
Directory,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DirectoryEntry<'buffer> {
pub name: &'buffer str,
pub object_type: DirectoryEntryType,
}
impl<'buffer, Context> Decode<'buffer, Context> for DirectoryEntry<'buffer> {
fn decode(
d: &mut minicbor::Decoder<'buffer>,
_: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
let name = d.str()?;
Ok(match name.strip_suffix('/') {
Some(name) => DirectoryEntry {
name,
object_type: DirectoryEntryType::Directory,
},
None => DirectoryEntry {
name,
object_type: DirectoryEntryType::File,
},
})
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum WriteMode {
Truncate,
Append,
}
impl WriteMode {
fn as_str(self) -> &'static str {
match self {
Self::Truncate => "w",
Self::Append => "a",
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Seek {
Set,
Current,
End,
}
impl<Context> Encode<Context> for Seek {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_: &mut Context,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.str(match self {
Self::Set => "set",
Self::Current => "cur",
Self::End => "end",
})?;
Ok(())
}
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ReadHandle {
address: Address,
descriptor: descriptor::Owned,
}
impl<'handle, 'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B>
for &'handle ReadHandle
{
type Locked = LockedReadHandle<'handle, 'invoker, 'buffer, B>;
fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
LockedReadHandle {
handle: self,
invoker,
buffer,
}
}
}
pub struct LockedReadHandle<'handle, 'invoker, 'buffer, B: Buffer> {
handle: &'handle ReadHandle,
invoker: &'invoker mut Invoker,
buffer: &'buffer mut B,
}
impl<'handle, 'invoker, 'buffer, B: Buffer> LockedReadHandle<'handle, 'invoker, 'buffer, B> {
pub async fn seek(&mut self, basis: Seek, offset: i64) -> Result<u64, Error> {
seek_impl(
self.invoker,
self.buffer,
&self.handle.address,
&self.handle.descriptor,
basis,
offset,
)
.await
}
pub async fn read(self, length: usize) -> Result<Option<&'buffer [u8]>, Error> {
use minicbor::bytes::ByteSlice;
#[derive(Encode)]
#[cbor(array)]
struct Params<'descriptor>(#[n(0)] &'descriptor descriptor::Owned, #[n(1)] usize);
let ret: Result<(Option<&'buffer ByteSlice>,), MethodCallError<'_>> = component_method(
self.invoker,
self.buffer,
&self.handle.address,
"read",
Some(&Params(&self.handle.descriptor, length)),
)
.await;
match ret {
Ok((Some(bytes),)) => Ok(Some(bytes)),
Ok((None,)) => Ok(None),
Err(MethodCallError::Other(exception)) => {
if exception.is_type("java.io.IOException") {
const NOT_ENOUGH_ENERGY: &str = "not enough energy";
let mut message_buffer = [0_u8; NOT_ENOUGH_ENERGY.len()];
match exception.message(&mut message_buffer) {
Ok(m) if m == NOT_ENOUGH_ENERGY => Err(Error::NotEnoughEnergy),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
} else {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
Err(e) => Err(e.into()),
}
}
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WriteHandle {
address: Address,
descriptor: descriptor::Owned,
}
impl<'handle, 'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B>
for &'handle WriteHandle
{
type Locked = LockedWriteHandle<'handle, 'invoker, 'buffer, B>;
fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
LockedWriteHandle {
handle: self,
invoker,
buffer,
}
}
}
pub struct LockedWriteHandle<'handle, 'invoker, 'buffer, B: Buffer> {
handle: &'handle WriteHandle,
invoker: &'invoker mut Invoker,
buffer: &'buffer mut B,
}
impl<'handle, 'invoker, 'buffer, B: Buffer> LockedWriteHandle<'handle, 'invoker, 'buffer, B> {
pub async fn seek(&mut self, basis: Seek, offset: i64) -> Result<u64, Error> {
seek_impl(
self.invoker,
self.buffer,
&self.handle.address,
&self.handle.descriptor,
basis,
offset,
)
.await
}
pub async fn write(&mut self, bytes: &[u8]) -> Result<(), Error> {
#[derive(Encode)]
#[cbor(array)]
struct Params<'descriptor, 'bytes>(
#[n(0)] &'descriptor descriptor::Owned,
#[n(1)] extref::Bytes<'bytes>,
);
let bytes = unsafe { extref::Bytes::new(bytes) };
let ret: Result<Ignore, MethodCallError<'_>> = component_method(
self.invoker,
self.buffer,
&self.handle.address,
"write",
Some(&Params(&self.handle.descriptor, bytes)),
)
.await;
match ret {
Ok(_) => Ok(()),
Err(MethodCallError::Other(exception)) => {
if exception.is_type("java.io.IOException") {
const NOT_ENOUGH_ENERGY: &str = "not enough energy";
const NOT_ENOUGH_SPACE: &str = "not enough space";
let mut message_buffer =
[0_u8; max_usize(NOT_ENOUGH_ENERGY.len(), NOT_ENOUGH_SPACE.len())];
match exception.message(&mut message_buffer) {
Ok(m) if m == NOT_ENOUGH_ENERGY => Err(Error::NotEnoughEnergy),
Ok(m) if m == NOT_ENOUGH_SPACE => Err(Error::DataTooLarge),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
} else {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
Err(e) => Err(e.into()),
}
}
}
async fn seek_impl<B: Buffer>(
invoker: &mut Invoker,
buffer: &mut B,
address: &Address,
descriptor: &descriptor::Owned,
basis: Seek,
offset: i64,
) -> Result<u64, Error> {
#[derive(Encode)]
#[cbor(array)]
struct Params<'descriptor>(
#[n(0)] &'descriptor descriptor::Owned,
#[n(1)] Seek,
#[n(2)] i64,
);
let ret: Result<(u64,), MethodCallError<'_>> = component_method(
invoker,
buffer,
address,
"seek",
Some(&Params(descriptor, basis, offset)),
)
.await;
match ret {
Ok((position,)) => Ok(position),
Err(MethodCallError::BadParameters(_)) => Err(Error::NegativeSeek),
Err(e) => Err(e.into()),
}
}