oc2_hlapi_v2 0.2.0

A client for the new version of OpenComputers II's HLAPI
Documentation
//! The scaner module's device and its associated types
//!
//! [OC2 Docs](https://fnuecke.github.io/oc2/manual/en_us/device/scanner.html)

use std::convert::Infallible;

use ndarray::{Array, Ix3};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{
    client::{DeviceBase, HlapiClient, HlapiClientError},
    devices::robot::RobotSide,
    messages::BlobMarker,
};

/// The result of an [`inspect`] call
///
/// [`inspect`]: Scanner::inspect
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct InspectResult {
    /// The block to the inspected side, if any
    pub block: Option<String>,
    /// The fluid to the inspected side, if any
    pub fluid: Option<String>,
    /// The entities directly adjacent to the inspected side, if any
    pub entities: Vec<String>,
}

#[derive(Deserialize, Debug, Clone)]
struct ScanResultRaw {
    hardness: BlobMarker,
    entities: Vec<String>,
}

#[derive(Debug, Clone)]
/// The result of a [`scan`] call
///
/// [`scan`]: Scanner::scan
pub struct ScanResult {
    /// The hardness of the blocks around the robot.
    ///
    /// Air is 0, fluids are 254, and unbreakable/unscannable blocks are 255. All other blocks are
    /// reported as values from 1 to 253.
    /// Axes are Y, then Z, then X, and 0,0,0 in the array is -3,-3,-3 in the scanned cube.
    /// See the official docs for more info.
    pub hardness: Array<u8, Ix3>,
    /// The entities in the scanned cube, if any
    pub entities: Vec<String>,
}

#[derive(Debug, Clone)]
/// The device for the scanner module
pub struct Scanner {
    client: HlapiClient,
    uuid: Uuid,
}

impl Scanner {
    /// Checks whether the robot can see full sky light
    pub fn can_see_sky(&self) -> Result<bool, HlapiClientError> {
        Ok(self.call("canSeeSky", &[], &[])?.0)
    }

    /// Inspects the given side of the robot and returns info about its contents.
    pub fn inspect(&self, side: RobotSide) -> Result<InspectResult, HlapiClientError> {
        Ok(self.call("inspect", &[&side as _], &[])?.0)
    }

    /// Gets the hardness of the blocks in a 7x7x7 cube centered around the robot,
    /// and also lists the entities in that cube.
    pub fn scan(&self) -> Result<ScanResult, HlapiClientError> {
        let (raw_result, blobs): (ScanResultRaw, _) = self.call("scan", &[], &[])?;
        let hardness_bytes = blobs
            .get(raw_result.hardness.blob_idx as usize)
            .ok_or(HlapiClientError::UnexpectedResponseType)?;
        if hardness_bytes.len() != 7 * 7 * 7 {
            return Err(HlapiClientError::UnexpectedResponseType);
        }

        let hardness =
            Array::from_shape_fn((7, 7, 7), |(y, z, x)| hardness_bytes[y * 7 * 7 + z * 7 + x]);

        Ok(ScanResult {
            hardness,
            entities: raw_result.entities,
        })
    }
}

impl DeviceBase for Scanner {
    const TYPE: &'static str = "scanner";

    type Event = Infallible;

    fn new(client: HlapiClient, uuid: Uuid) -> Result<Self, HlapiClientError> {
        Ok(Self { client, uuid })
    }

    fn uuid_base(&self) -> Uuid {
        self.uuid
    }

    fn client(&self) -> &HlapiClient {
        &self.client
    }
}