use crate::error::Error;
use crate::helpers::{max_of_usizes, Ignore};
use alloc::vec::Vec;
use core::num::NonZeroU32;
use minicbor::{Decode, Decoder};
use oc_wasm_futures::invoke::{component_method, value_method, Buffer};
use oc_wasm_helpers::{
error::NullAndStringOr,
fluid::{Fluid, Tank},
inventory::{ItemStack, OptionItemStack},
sides::{Relative as RelativeSide, Side},
Lockable,
};
use oc_wasm_safe::{
component::{Invoker, MethodCallError},
descriptor, Address,
};
pub use super::robot::ActionSide;
pub const INVENTORY_CONTROLLER_TYPE: &str = "inventory_controller";
pub const TANK_CONTROLLER_TYPE: &str = "tank_controller";
pub const TRANSPOSER_TYPE: &str = "transposer";
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Controller(Address);
impl Controller {
#[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 Controller {
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_inventory_size(&mut self, side: impl Side) -> Result<u32, Error> {
let side: u8 = side.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getInventorySize",
Some(&(side,)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_slot_stack_size(
&mut self,
side: impl Side,
slot: NonZeroU32,
) -> Result<u32, Error> {
let side: u8 = side.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getSlotStackSize",
Some(&(side, slot)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_slot_max_stack_size(
&mut self,
side: impl Side,
slot: NonZeroU32,
) -> Result<Option<NonZeroU32>, Error> {
let side: u8 = side.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getSlotMaxStackSize",
Some(&(side, slot)),
)
.await,
)?;
Ok(NonZeroU32::new(ret.0))
}
pub async fn compare_stacks(
&mut self,
side: impl Side,
slot_a: NonZeroU32,
slot_b: NonZeroU32,
check_nbt: bool,
) -> Result<bool, Error> {
let side: u8 = side.into();
let ret: (bool,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"compareStacks",
Some(&(side, slot_a, slot_b, check_nbt)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn are_stacks_equivalent(
&mut self,
side: impl Side,
slot_a: NonZeroU32,
slot_b: NonZeroU32,
) -> Result<bool, Error> {
let side: u8 = side.into();
let ret: (bool,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"areStacksEquivalent",
Some(&(side, slot_a, slot_b)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_stack_in_slot(
self,
side: impl Side,
slot: NonZeroU32,
) -> Result<Option<ItemStack<'buffer>>, Error> {
let side: u8 = side.into();
let ret: (Option<ItemStack<'buffer>>,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getStackInSlot",
Some(&(side, slot)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_all_stacks(&mut self, side: impl Side) -> Result<Snapshot, Error> {
let side: u8 = side.into();
let ret: (descriptor::Decoded,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getAllStacks",
Some(&(side,)),
)
.await,
)?;
let descriptor = ret.0;
let descriptor = unsafe { descriptor.into_owned() };
Ok(Snapshot(descriptor))
}
pub async fn get_inventory_name(self, side: impl Side) -> Result<&'buffer str, Error> {
let side: u8 = side.into();
let ret: (&'buffer str,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getInventoryName",
Some(&(side,)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn transfer_item<SideType: Side>(
&mut self,
source: SideType,
sink: SideType,
count: u32,
) -> Result<u32, Error> {
let source: u8 = source.into();
let sink: u8 = sink.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"transferItem",
Some(&(source, sink, count)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn transfer_item_from_slot<SideType: Side>(
&mut self,
source: SideType,
sink: SideType,
count: u32,
source_slot: NonZeroU32,
) -> Result<u32, Error> {
let source: u8 = source.into();
let sink: u8 = sink.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"transferItem",
Some(&(source, sink, count, source_slot.get())),
)
.await,
)?;
Ok(ret.0)
}
pub async fn transfer_item_from_slot_to_slot<SideType: Side>(
&mut self,
source: SideType,
sink: SideType,
count: u32,
source_slot: NonZeroU32,
sink_slot: NonZeroU32,
) -> Result<u32, Error> {
let source: u8 = source.into();
let sink: u8 = sink.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"transferItem",
Some(&(source, sink, count, source_slot.get(), sink_slot.get())),
)
.await,
)?;
Ok(ret.0)
}
pub async fn transfer_fluid<SideType: Side>(
&mut self,
source: SideType,
sink: SideType,
count: u32,
) -> Result<u32, Error> {
let source: u8 = source.into();
let sink: u8 = sink.into();
let ret: (bool, u32) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"transferFluid",
Some(&(source, sink, count)),
)
.await,
)?;
Ok(ret.1)
}
pub async fn get_tank_level(
&mut self,
side: impl Side,
tank: NonZeroU32,
) -> Result<u32, Error> {
let side: u8 = side.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getTankLevel",
Some(&(side, tank.get())),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_tank_capacity(
&mut self,
side: impl Side,
tank: NonZeroU32,
) -> Result<u32, Error> {
let side: u8 = side.into();
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getTankCapacity",
Some(&(side, tank.get())),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_fluid_in_tank(
self,
side: impl Side,
tank: NonZeroU32,
) -> Result<Tank<'buffer>, Error> {
let side: u8 = side.into();
let ret: (Tank<'buffer>,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getFluidInTank",
Some(&(side, tank.get())),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_fluids_in_tanks(self, side: impl Side) -> Result<Vec<Tank<'buffer>>, Error> {
let side: u8 = side.into();
let ret: (Vec<Tank<'buffer>>,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getFluidInTank",
Some(&(side,)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_tank_level_in_slot(&mut self, slot: NonZeroU32) -> Result<u32, Error> {
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getTankLevelInSlot",
Some(&(slot.get(),)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_tank_level_in_selected_slot(&mut self) -> Result<u32, Error> {
let ret: (u32,) = Self::map_errors(
component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getTankLevelInSlot",
None,
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_tank_capacity_in_slot(&mut self, slot: NonZeroU32) -> Result<u32, Error> {
let ret: (u32,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getTankCapacityInSlot",
Some(&(slot.get(),)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_tank_capacity_in_selected_slot(&mut self) -> Result<u32, Error> {
let ret: (u32,) = Self::map_errors(
component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getTankCapacityInSlot",
None,
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_fluid_in_tank_in_slot(
self,
slot: NonZeroU32,
) -> Result<Option<Fluid<'buffer>>, Error> {
let ret: (Option<Fluid<'buffer>>,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getFluidInTankInSlot",
Some(&(slot.get(),)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_fluid_in_tank_in_selected_slot(self) -> Result<Option<Fluid<'buffer>>, Error> {
let ret: (Option<Fluid<'buffer>>,) = Self::map_errors(
component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getFluidInTankInSlot",
None,
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_fluid_in_internal_tank(
self,
tank: NonZeroU32,
) -> Result<Option<Fluid<'buffer>>, Error> {
let ret: (Option<Fluid<'buffer>>,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getFluidInInternalTank",
Some(&(tank.get(),)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_fluid_in_selected_internal_tank(
self,
) -> Result<Option<Fluid<'buffer>>, Error> {
let ret: (Option<Fluid<'buffer>>,) = Self::map_errors(
component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getFluidInInternalTank",
None,
)
.await,
)?;
Ok(ret.0)
}
pub async fn drain(&mut self, amount: NonZeroU32) -> Result<u32, Error> {
self.drain_or_fill(amount, "drain").await
}
pub async fn fill(&mut self, amount: NonZeroU32) -> Result<u32, Error> {
self.drain_or_fill(amount, "fill").await
}
pub async fn get_stack_in_internal_slot(
self,
slot: NonZeroU32,
) -> Result<Option<ItemStack<'buffer>>, Error> {
let ret: (Option<ItemStack<'buffer>>,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"getStackInInternalSlot",
Some(&(slot,)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn get_stack_in_selected_internal_slot(
self,
) -> Result<Option<ItemStack<'buffer>>, Error> {
let ret: (Option<ItemStack<'buffer>>,) = Self::map_errors(
component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getStackInInternalSlot",
None,
)
.await,
)?;
Ok(ret.0)
}
pub async fn is_equivalent_to(&mut self, slot: NonZeroU32) -> Result<bool, Error> {
let ret: (bool,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"isEquivalentTo",
Some(&(slot.get(),)),
)
.await,
)?;
Ok(ret.0)
}
pub async fn drop_into_slot(
&mut self,
side: ActionSide,
slot: NonZeroU32,
count: u32,
face: Option<RelativeSide>,
) -> Result<(), Error> {
let side = u8::from(side);
let slot = slot.get();
let ret: (bool, Option<&str>) = Self::map_errors(if let Some(f) = face {
component_method(
self.invoker,
self.buffer,
&self.address,
"dropIntoSlot",
Some(&(side, slot, count, u8::from(f))),
)
.await
} else {
component_method(
self.invoker,
self.buffer,
&self.address,
"dropIntoSlot",
Some(&(side, slot, count)),
)
.await
})?;
match ret {
(true, _) => Ok(()),
(false, Some("inventory full/invalid slot")) => Err(Error::InventoryFull),
(false, _) => Err(Error::Failed),
}
}
pub async fn suck_from_slot(
&mut self,
side: ActionSide,
slot: NonZeroU32,
count: u32,
face: Option<RelativeSide>,
) -> Result<u32, Error> {
struct FalseOrU32(u32);
impl<Context> Decode<'_, Context> for FalseOrU32 {
fn decode(
d: &mut Decoder<'_>,
_: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
if d.datatype()? == minicbor::data::Type::Bool {
if d.bool()? {
Err(minicbor::decode::Error::message(
"expected only false, not true",
))
} else {
Ok(Self(0))
}
} else {
Ok(Self(d.u32()?))
}
}
}
let side = u8::from(side);
let slot = slot.get();
let ret: (FalseOrU32,) = Self::map_errors(if let Some(f) = face {
component_method(
self.invoker,
self.buffer,
&self.address,
"suckFromSlot",
Some(&(side, slot, count, u8::from(f))),
)
.await
} else {
component_method(
self.invoker,
self.buffer,
&self.address,
"suckFromSlot",
Some(&(side, slot, count)),
)
.await
})?;
Ok(ret.0 .0)
}
pub async fn equip(&mut self) -> Result<(), Error> {
let ret: (bool,) =
component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "equip", None)
.await?;
if ret.0 {
Ok(())
} else {
Err(Error::NoInventory)
}
}
async fn drain_or_fill(&mut self, amount: NonZeroU32, method: &str) -> Result<u32, Error> {
let ret: (bool, u32) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
method,
Some(&(amount.get(),)),
)
.await,
)?;
Ok(ret.1)
}
fn map_errors<T>(x: Result<NullAndStringOr<'_, T>, MethodCallError<'_>>) -> Result<T, Error> {
const INCOMPATIBLE_FLUID: &str = "incompatible fluid";
const INCOMPATIBLE_OR_NO_FLUID: &str = "incompatible or no fluid";
const INVALID_SLOT: &str = "invalid slot";
const INVALID_TANK_INDEX: &str = "invalid tank index";
const ITEM_IS_EMPTY_OR_NOT_A_FLUID_CONTAINER: &str =
"item is empty or not a fluid container";
const ITEM_IS_FULL_OR_NOT_A_FLUID_CONTAINER: &str = "item is full or not a fluid container";
const ITEM_IS_NOT_A_FLUID_CONTAINER: &str = "item is not a fluid container";
const NO_INVENTORY: &str = "no inventory";
const NO_TANK: &str = "no tank";
const NOT_ENOUGH_ENERGY: &str = "not enough energy";
const NOT_ENABLED_IN_CONFIG: &str = "not enabled in config";
const TANK_IS_EMPTY: &str = "tank is empty";
const TANK_IS_FULL: &str = "tank is full";
const UNKNOWN: &str = "Unknown";
const ERROR_MESSAGE_BUFFER_SIZE: usize = max_of_usizes(&[
INCOMPATIBLE_FLUID.len(),
INCOMPATIBLE_OR_NO_FLUID.len(),
INVALID_SLOT.len(),
INVALID_TANK_INDEX.len(),
ITEM_IS_EMPTY_OR_NOT_A_FLUID_CONTAINER.len(),
ITEM_IS_FULL_OR_NOT_A_FLUID_CONTAINER.len(),
ITEM_IS_NOT_A_FLUID_CONTAINER.len(),
NO_INVENTORY.len(),
NO_TANK.len(),
NOT_ENOUGH_ENERGY.len(),
NOT_ENABLED_IN_CONFIG.len(),
TANK_IS_FULL.len(),
UNKNOWN.len(),
]);
match x {
Ok(NullAndStringOr::Ok(x)) => Ok(x),
Ok(NullAndStringOr::Err(
INCOMPATIBLE_FLUID | INCOMPATIBLE_OR_NO_FLUID | TANK_IS_EMPTY,
)) => Err(Error::Failed),
Ok(NullAndStringOr::Err(INVALID_SLOT | INVALID_TANK_INDEX)) => {
Err(Error::BadInventorySlot)
}
Ok(NullAndStringOr::Err(
ITEM_IS_EMPTY_OR_NOT_A_FLUID_CONTAINER
| ITEM_IS_FULL_OR_NOT_A_FLUID_CONTAINER
| ITEM_IS_NOT_A_FLUID_CONTAINER,
)) => Err(Error::BadItem),
Ok(NullAndStringOr::Err(NO_INVENTORY | NO_TANK | UNKNOWN)) => Err(Error::NoInventory),
Ok(NullAndStringOr::Err(NOT_ENOUGH_ENERGY)) => Err(Error::NotEnoughEnergy),
Ok(NullAndStringOr::Err(NOT_ENABLED_IN_CONFIG)) => Err(Error::Unsupported),
Ok(NullAndStringOr::Err(TANK_IS_FULL)) => Err(Error::InventoryFull),
Ok(NullAndStringOr::Err(_)) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
Err(MethodCallError::BadParameters(exception)) => {
let mut buffer = [0_u8; ERROR_MESSAGE_BUFFER_SIZE];
match exception.message(&mut buffer) {
Ok(INVALID_SLOT | INVALID_TANK_INDEX) => Err(Error::BadInventorySlot),
Ok(NO_INVENTORY | NO_TANK | UNKNOWN) => Err(Error::NoInventory),
Ok(NOT_ENOUGH_ENERGY) => Err(Error::NotEnoughEnergy),
Ok(NOT_ENABLED_IN_CONFIG) => Err(Error::Unsupported),
_ => Err(Error::BadComponent(
oc_wasm_safe::error::Error::BadParameters,
)),
}
}
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Snapshot(pub descriptor::Owned);
impl<'handle, 'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B>
for &'handle Snapshot
{
type Locked = LockedSnapshot<'handle, 'invoker, 'buffer, B>;
fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
use oc_wasm_safe::descriptor::AsDescriptor;
LockedSnapshot {
descriptor: self.0.as_descriptor(),
invoker,
buffer,
}
}
}
pub struct LockedSnapshot<'snapshot, 'invoker, 'buffer, B: Buffer> {
descriptor: descriptor::Borrowed<'snapshot>,
invoker: &'invoker mut Invoker,
buffer: &'buffer mut B,
}
impl<'snapshot, 'invoker, 'buffer, B: Buffer> LockedSnapshot<'snapshot, 'invoker, 'buffer, B> {
pub async fn next(self) -> Result<Option<ItemStack<'buffer>>, Error> {
let ret: Vec<OptionItemStack<'buffer>> = oc_wasm_futures::invoke::value::<(), _, _, _>(
self.invoker,
self.buffer,
&self.descriptor,
None,
)
.await?;
if let Some(elt) = ret.into_iter().next() {
Ok(elt.0)
} else {
Err(Error::BadInventorySlot)
}
}
pub async fn get(self, slot: NonZeroU32) -> Result<Option<ItemStack<'buffer>>, Error> {
let ret: (OptionItemStack<'buffer>,) = oc_wasm_futures::invoke::value_indexed_read(
self.invoker,
self.buffer,
&self.descriptor,
Some(&(slot,)),
)
.await?;
Ok(ret.0.into())
}
pub async fn reset(&mut self) -> Result<(), Error> {
value_method::<(), Ignore, _, _>(
self.invoker,
self.buffer,
&self.descriptor,
"reset",
None,
)
.await?;
Ok(())
}
pub async fn count(&mut self) -> Result<u32, Error> {
let ret: (u32,) =
value_method::<(), _, _, _>(self.invoker, self.buffer, &self.descriptor, "count", None)
.await?;
Ok(ret.0)
}
pub async fn get_all(self) -> Result<Vec<Option<ItemStack<'buffer>>>, Error> {
#[derive(Decode)]
struct Return<'buffer> {
#[b(0)]
#[cbor(
decode_with = "oc_wasm_helpers::decode_one_based_map_as_vector::<Ctx, OptionItemStack<'_>, Option<ItemStack<'_>>>"
)]
x: Vec<Option<ItemStack<'buffer>>>,
}
let ret: Return<'buffer> = value_method::<(), _, _, _>(
self.invoker,
self.buffer,
&self.descriptor,
"getAll",
None,
)
.await?;
Ok(ret.x)
}
}