use crate::common::Rgb;
use crate::error::Error;
use core::convert::TryFrom;
use core::fmt::{Display, Formatter};
use core::num::NonZeroU32;
use core::str::FromStr;
use minicbor::Decode;
use oc_wasm_futures::invoke::{component_method, Buffer};
use oc_wasm_helpers::{
error::NullAndStringOr,
sides::{Relative as RelativeSide, Side},
Lockable,
};
use oc_wasm_safe::{
component::{Invoker, MethodCallError},
Address,
};
pub const TYPE: &str = "robot";
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TryFromRelativeSideError(());
impl Display for TryFromRelativeSideError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
"invalid value".fmt(f)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromRelativeSideError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum MoveDirection {
Down,
Up,
Back,
Front,
}
impl From<MoveDirection> for u8 {
fn from(x: MoveDirection) -> Self {
match x {
MoveDirection::Down => 0,
MoveDirection::Up => 1,
MoveDirection::Back => 2,
MoveDirection::Front => 3,
}
}
}
impl From<MoveDirection> for usize {
fn from(x: MoveDirection) -> Self {
u8::from(x) as usize
}
}
impl From<MoveDirection> for RelativeSide {
fn from(x: MoveDirection) -> Self {
match x {
MoveDirection::Down => Self::Bottom,
MoveDirection::Up => Self::Top,
MoveDirection::Back => Self::Back,
MoveDirection::Front => Self::Front,
}
}
}
impl TryFrom<u8> for MoveDirection {
type Error = oc_wasm_helpers::error::TryFromInt;
fn try_from(x: u8) -> Result<Self, Self::Error> {
match x {
0 => Ok(Self::Down),
1 => Ok(Self::Up),
2 => Ok(Self::Back),
3 => Ok(Self::Front),
_ => Err(oc_wasm_helpers::error::TryFromInt),
}
}
}
impl TryFrom<RelativeSide> for MoveDirection {
type Error = TryFromRelativeSideError;
fn try_from(x: RelativeSide) -> Result<Self, Self::Error> {
match x {
RelativeSide::Bottom => Ok(Self::Down),
RelativeSide::Top => Ok(Self::Up),
RelativeSide::Back => Ok(Self::Back),
RelativeSide::Front => Ok(Self::Front),
_ => Err(TryFromRelativeSideError(())),
}
}
}
impl From<ActionSide> for MoveDirection {
fn from(x: ActionSide) -> Self {
match x {
ActionSide::Bottom => Self::Down,
ActionSide::Top => Self::Up,
ActionSide::Front => Self::Front,
}
}
}
impl Side for MoveDirection {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ActionSide {
Bottom,
Top,
Front,
}
impl From<ActionSide> for u8 {
fn from(x: ActionSide) -> Self {
match x {
ActionSide::Bottom => 0,
ActionSide::Top => 1,
ActionSide::Front => 3,
}
}
}
impl From<ActionSide> for usize {
fn from(x: ActionSide) -> Self {
u8::from(x) as usize
}
}
impl From<ActionSide> for RelativeSide {
fn from(x: ActionSide) -> Self {
match x {
ActionSide::Bottom => Self::Bottom,
ActionSide::Top => Self::Top,
ActionSide::Front => Self::Front,
}
}
}
impl TryFrom<u8> for ActionSide {
type Error = oc_wasm_helpers::error::TryFromInt;
fn try_from(x: u8) -> Result<Self, Self::Error> {
match x {
0 => Ok(Self::Bottom),
1 => Ok(Self::Top),
3 => Ok(Self::Front),
_ => Err(oc_wasm_helpers::error::TryFromInt),
}
}
}
impl TryFrom<RelativeSide> for ActionSide {
type Error = TryFromRelativeSideError;
fn try_from(x: RelativeSide) -> Result<Self, Self::Error> {
match x {
RelativeSide::Bottom => Ok(Self::Bottom),
RelativeSide::Top => Ok(Self::Top),
RelativeSide::Front => Ok(Self::Front),
_ => Err(TryFromRelativeSideError(())),
}
}
}
impl Side for ActionSide {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Rotation {
Clockwise,
Counterclockwise,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ToolHit {
Entity,
Block,
Fire,
Air,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ActivateResult {
BlockActivated,
ItemPlaced,
ItemUsed,
ItemInteracted,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum BlockContent {
Entity,
Air,
Liquid,
Replaceable,
Passable,
Solid,
}
impl BlockContent {
#[must_use = "This function is only useful for its return value"]
pub fn as_str(&self) -> &'static str {
match self {
Self::Entity => "entity",
Self::Air => "air",
Self::Liquid => "liquid",
Self::Replaceable => "replaceable",
Self::Passable => "passable",
Self::Solid => "solid",
}
}
}
impl Display for BlockContent {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
self.as_str().fmt(f)
}
}
impl FromStr for BlockContent {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"entity" => Ok(Self::Entity),
"air" => Ok(Self::Air),
"liquid" => Ok(Self::Liquid),
"replaceable" => Ok(Self::Replaceable),
"passable" => Ok(Self::Passable),
"solid" => Ok(Self::Solid),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
}
}
impl TryFrom<&str> for BlockContent {
type Error = <Self as FromStr>::Err;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::from_str(s)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Robot(Address);
impl Robot {
#[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 Robot {
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_light_colour(&mut self) -> Result<Rgb, Error> {
let ret: (u32,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getLightColor",
None,
)
.await?;
Ok(Rgb(ret.0))
}
pub async fn set_light_colour(&mut self, colour: Rgb) -> Result<(), Error> {
component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"setLightColor",
Some(&(colour.0,)),
)
.await?;
Ok(())
}
pub async fn durability(&mut self) -> Result<Option<f64>, Error> {
let ret: NullAndStringOr<'_, (f64,)> = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"durability",
None,
)
.await?;
Ok(match ret {
NullAndStringOr::Ok(v) => Some(v.0),
NullAndStringOr::Err(_) => None,
})
}
pub async fn move_robot(&mut self, direction: MoveDirection) -> Result<(), Error> {
let ret: NullAndStringOr<'_, (bool,)> = component_method(
self.invoker,
self.buffer,
&self.address,
"move",
Some(&(u8::from(direction),)),
)
.await?;
match ret {
NullAndStringOr::Ok(_) => Ok(()),
NullAndStringOr::Err("not enough energy") => Err(Error::NotEnoughEnergy),
NullAndStringOr::Err("impossible move") => Err(Error::ImpossibleMove),
NullAndStringOr::Err(s) => Err(Error::Blocked(s.parse()?)),
}
}
pub async fn turn(&mut self, direction: Rotation) -> Result<(), Error> {
let ret: NullAndStringOr<'_, (bool,)> = component_method(
self.invoker,
self.buffer,
&self.address,
"turn",
Some(&(direction == Rotation::Clockwise,)),
)
.await?;
match ret {
NullAndStringOr::Ok(_) => Ok(()),
NullAndStringOr::Err("not enough energy") => Err(Error::NotEnoughEnergy),
NullAndStringOr::Err(_) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
}
pub async fn name(self) -> Result<&'buffer str, Error> {
Ok(component_method::<(), (&'buffer str,), _>(
self.invoker,
self.buffer,
&self.address,
"name",
None,
)
.await?
.0)
}
pub async fn swing(
&mut self,
side: ActionSide,
face: Option<RelativeSide>,
sneak: bool,
) -> Result<ToolHit, Error> {
let ret: (bool, &str) = component_method(
self.invoker,
self.buffer,
&self.address,
"swing",
Some(&(u8::from(side), face.map(u8::from), sneak)),
)
.await?;
if ret.1 == "entity" {
Ok(ToolHit::Entity)
} else if ret.1 == "block" {
if ret.0 {
Ok(ToolHit::Block)
} else {
Err(Error::BadItem)
}
} else if ret.1 == "fire" {
Ok(ToolHit::Fire)
} else if ret.1 == "air" {
Ok(ToolHit::Air)
} else {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
pub async fn use_item(
&mut self,
side: ActionSide,
face: Option<RelativeSide>,
sneak: bool,
duration: f64,
) -> Result<ActivateResult, Error> {
let ret: (bool, Option<&str>) = component_method(
self.invoker,
self.buffer,
&self.address,
"use",
Some(&(u8::from(side), face.map(u8::from), sneak, duration)),
)
.await?;
if ret.1 == Some("block_activated") {
Ok(ActivateResult::BlockActivated)
} else if ret.1 == Some("item_placed") {
Ok(ActivateResult::ItemPlaced)
} else if ret.1 == Some("item_used") {
Ok(ActivateResult::ItemUsed)
} else if ret.1 == Some("item_interacted") {
Ok(ActivateResult::ItemInteracted)
} else if ret.0 {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
} else {
Err(Error::Failed)
}
}
pub async fn place(
&mut self,
side: ActionSide,
face: Option<RelativeSide>,
sneak: bool,
) -> Result<(), Error> {
let ret: NullAndStringOr<'_, (bool,)> = component_method(
self.invoker,
self.buffer,
&self.address,
"place",
Some(&(u8::from(side), face.map(u8::from), sneak)),
)
.await?;
match ret {
NullAndStringOr::Ok((true,)) => Ok(()),
NullAndStringOr::Ok((false,)) => Err(Error::Failed),
NullAndStringOr::Err("nothing selected") => Err(Error::BadItem),
NullAndStringOr::Err(_) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
}
pub async fn detect(&mut self, side: ActionSide) -> Result<BlockContent, Error> {
let ret: (bool, &str) = component_method(
self.invoker,
self.buffer,
&self.address,
"detect",
Some(&(u8::from(side),)),
)
.await?;
ret.1.parse()
}
pub async fn inventory_size(&mut self) -> Result<u32, Error> {
Ok(component_method::<(), (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"inventorySize",
None,
)
.await?
.0)
}
pub async fn selected(&mut self) -> Result<NonZeroU32, Error> {
let ret: (u32,) =
component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "select", None)
.await?;
match NonZeroU32::new(ret.0) {
Some(n) => Ok(n),
None => Err(Error::NoInventory),
}
}
pub async fn select(&mut self, slot: NonZeroU32) -> Result<(), Error> {
let ret = component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"select",
Some(&(slot,)),
)
.await;
match ret {
Ok(_) => Ok(()),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn count(&mut self, slot: NonZeroU32) -> Result<u32, Error> {
let ret = component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"count",
Some(&(slot,)),
)
.await;
match ret {
Ok((n,)) => Ok(n),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn count_selected(&mut self) -> Result<u32, Error> {
Ok(component_method::<(), (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"count",
None,
)
.await?
.0)
}
pub async fn space(&mut self, slot: NonZeroU32) -> Result<u32, Error> {
let ret = component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"space",
Some(&(slot,)),
)
.await;
match ret {
Ok((n,)) => Ok(n),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn space_selected(&mut self) -> Result<u32, Error> {
Ok(component_method::<(), (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"space",
None,
)
.await?
.0)
}
pub async fn compare_to(&mut self, other_slot: NonZeroU32, nbt: bool) -> Result<bool, Error> {
let ret = component_method::<_, (bool,), _>(
self.invoker,
self.buffer,
&self.address,
"compareTo",
Some(&(other_slot, nbt)),
)
.await;
match ret {
Ok((f,)) => Ok(f),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn transfer_to(&mut self, target_slot: NonZeroU32, amount: u32) -> Result<(), Error> {
let ret = component_method::<_, (bool,), _>(
self.invoker,
self.buffer,
&self.address,
"transferTo",
Some(&(target_slot, amount)),
)
.await;
match ret {
Ok((true,)) => Ok(()),
Ok((false,)) => Err(Error::Failed),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn compare(&mut self, side: ActionSide, fuzzy: bool) -> Result<bool, Error> {
Ok(component_method::<_, (bool,), _>(
self.invoker,
self.buffer,
&self.address,
"compare",
Some(&(u8::from(side), fuzzy)),
)
.await?
.0)
}
pub async fn drop(&mut self, side: ActionSide, count: u32) -> Result<(), Error> {
let ret = component_method::<_, (bool, Option<&str>), _>(
self.invoker,
self.buffer,
&self.address,
"drop",
Some(&(u8::from(side), count)),
)
.await;
match ret {
Ok((true, _)) => Ok(()),
Ok((false, None)) => Err(Error::NoItem),
Ok((false, Some("inventory full"))) => Err(Error::InventoryFull),
Ok((false, Some(_))) => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn suck(&mut self, side: ActionSide, count: u32) -> Result<u32, Error> {
enum BoolOrU32 {
Bool(bool),
U32(u32),
}
impl<Context> Decode<'_, Context> for BoolOrU32 {
fn decode(
d: &mut minicbor::Decoder<'_>,
_: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
if d.datatype()? == minicbor::data::Type::Bool {
Ok(Self::Bool(d.bool()?))
} else {
Ok(Self::U32(d.u32()?))
}
}
}
let ret: (BoolOrU32,) = component_method(
self.invoker,
self.buffer,
&self.address,
"suck",
Some(&(u8::from(side), count)),
)
.await?;
match ret.0 {
BoolOrU32::Bool(false) => Err(Error::Failed),
BoolOrU32::Bool(true) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
BoolOrU32::U32(count) => Ok(count),
}
}
pub async fn tank_count(&mut self) -> Result<u32, Error> {
Ok(component_method::<(), (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"tankCount",
None,
)
.await?
.0)
}
pub async fn selected_tank(&mut self) -> Result<NonZeroU32, Error> {
Ok(component_method::<(), (NonZeroU32,), _>(
self.invoker,
self.buffer,
&self.address,
"selectTank",
None,
)
.await?
.0)
}
pub async fn select_tank(&mut self, tank: NonZeroU32) -> Result<(), Error> {
let ret = component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"selectTank",
Some(&(tank,)),
)
.await;
match ret {
Ok(_) => Ok(()),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn tank_level(&mut self, tank: NonZeroU32) -> Result<u32, Error> {
let ret = component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"tankLevel",
Some(&(tank,)),
)
.await;
match ret {
Ok((n,)) => Ok(n),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn tank_level_selected(&mut self) -> Result<u32, Error> {
Ok(component_method::<(), (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"tankLevel",
None,
)
.await?
.0)
}
pub async fn tank_space(&mut self, tank: NonZeroU32) -> Result<u32, Error> {
let ret = component_method::<_, (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"tankSpace",
Some(&(tank,)),
)
.await;
match ret {
Ok((n,)) => Ok(n),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn tank_space_selected(&mut self) -> Result<u32, Error> {
Ok(component_method::<(), (u32,), _>(
self.invoker,
self.buffer,
&self.address,
"tankSpace",
None,
)
.await?
.0)
}
pub async fn compare_fluid_to(&mut self, other_tank: NonZeroU32) -> Result<bool, Error> {
let ret = component_method::<_, (bool,), _>(
self.invoker,
self.buffer,
&self.address,
"compareFluidTo",
Some(&(other_tank,)),
)
.await;
match ret {
Ok((f,)) => Ok(f),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
pub async fn transfer_fluid_to(
&mut self,
target_tank: NonZeroU32,
amount: u32,
) -> Result<(), Error> {
let ret = component_method::<_, NullAndStringOr<'_, bool>, _>(
self.invoker,
self.buffer,
&self.address,
"transferFluidTo",
Some(&(target_tank, amount)),
)
.await;
match ret {
Ok(NullAndStringOr::Ok(true)) => Ok(()),
Ok(NullAndStringOr::Err("incompatible or no fluid")) => Err(Error::Failed),
Ok(NullAndStringOr::Err("invalid index")) | Err(MethodCallError::BadParameters(_)) => {
Err(Error::BadInventorySlot)
}
Ok(NullAndStringOr::Ok(false) | NullAndStringOr::Err(_)) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
Err(e) => Err(e.into()),
}
}
pub async fn compare_fluid(
&mut self,
side: ActionSide,
tank: NonZeroU32,
) -> Result<bool, Error> {
let ret = component_method::<_, (bool,), _>(
self.invoker,
self.buffer,
&self.address,
"compareFluid",
Some(&(u8::from(side), tank)),
)
.await;
match ret {
Ok((b,)) => Ok(b),
Err(MethodCallError::BadParameters(_)) => Err(Error::BadInventorySlot),
Err(e) => Err(e.into()),
}
}
pub async fn drain(&mut self, side: ActionSide, amount: u32) -> Result<u32, Error> {
self.drain_or_fill(side, amount, "drain").await
}
pub async fn fill(&mut self, side: ActionSide, amount: u32) -> Result<u32, Error> {
self.drain_or_fill(side, amount, "fill").await
}
async fn drain_or_fill(
&mut self,
side: ActionSide,
amount: u32,
method: &str,
) -> Result<u32, Error> {
let ret: NullAndStringOr<'_, (bool, u32)> = component_method(
self.invoker,
self.buffer,
&self.address,
method,
Some(&(u8::from(side), amount)),
)
.await?;
match ret {
NullAndStringOr::Ok((_, n)) => Ok(n),
NullAndStringOr::Err("incompatible or no fluid") => Err(Error::BadItem),
NullAndStringOr::Err("no space" | "tank is full") => Err(Error::InventoryFull),
NullAndStringOr::Err("no tank selected") => Err(Error::NoInventory),
NullAndStringOr::Err("tank is empty") => Err(Error::NoItem),
NullAndStringOr::Err(_) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
}
}