cat-dev 0.0.14

A library for interacting with the CAT-DEV hardware units distributed by Nintendo (i.e. a type of Wii-U DevKits).
Documentation
//! Definitions for the `GetFilePosition` packet type, and it's response types.
//!
//! This returns the current byte position of the file as a u32, and will CLAMP
//! any values above it.

use crate::errors::NetworkParseError;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::fmt::{Display, Formatter, Result as FmtResult};
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};

/// A packet to get information about an open file handle.
///
/// This can do everything from "get the free space of the disk this path
/// is on", to "get my some metadata about this very specific path."
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SataGetFilePositionPacketBody {
	file_descriptor: i32,
}

impl SataGetFilePositionPacketBody {
	/// Create a new packet to get the file position to a particular file.
	#[must_use]
	pub const fn new(file_descriptor: i32) -> Self {
		Self { file_descriptor }
	}

	#[must_use]
	pub const fn file_descriptor(&self) -> i32 {
		self.file_descriptor
	}

	pub const fn set_file_descriptor(&mut self, new_fd: i32) {
		self.file_descriptor = new_fd;
	}
}

impl From<&SataGetFilePositionPacketBody> for Bytes {
	fn from(value: &SataGetFilePositionPacketBody) -> Self {
		let mut buff = BytesMut::with_capacity(4);
		buff.put_i32(value.file_descriptor);
		buff.freeze()
	}
}

impl From<SataGetFilePositionPacketBody> for Bytes {
	fn from(value: SataGetFilePositionPacketBody) -> Self {
		Self::from(&value)
	}
}

impl TryFrom<Bytes> for SataGetFilePositionPacketBody {
	type Error = NetworkParseError;

	fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
		if value.len() < 0x4 {
			return Err(NetworkParseError::FieldNotLongEnough(
				"SataGetFilePosition",
				"Body",
				0x4,
				value.len(),
				value,
			));
		}
		if value.len() > 0x4 {
			return Err(NetworkParseError::UnexpectedTrailer(
				"SataGetFilePosition",
				value.slice(0x4..),
			));
		}

		let fd = value.get_i32();

		Ok(Self {
			file_descriptor: fd,
		})
	}
}

const SATA_GET_FILE_POSITION_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("fd")];

impl Structable for SataGetFilePositionPacketBody {
	fn definition(&self) -> StructDef<'_> {
		StructDef::new_static(
			"SataStatFilePacketBody",
			Fields::Named(SATA_GET_FILE_POSITION_PACKET_BODY_FIELDS),
		)
	}
}

impl Valuable for SataGetFilePositionPacketBody {
	fn as_value(&self) -> Value<'_> {
		Value::Structable(self)
	}

	fn visit(&self, visitor: &mut dyn Visit) {
		visitor.visit_named_fields(&NamedValues::new(
			SATA_GET_FILE_POSITION_PACKET_BODY_FIELDS,
			&[Valuable::as_value(&self.file_descriptor)],
		));
	}
}

/// A file descriptor from a sata endpoint that optionally includes a single
/// result code in back.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
pub struct SataGetFilePositionResult {
	/// `Ok()` is the current file position.
	/// `Err()` is an error code.
	position: Result<u32, u32>,
}

impl SataGetFilePositionResult {
	/// A successful file position, with a 0 error code.
	#[must_use]
	pub const fn success(pos: u32) -> Self {
		Self { position: Ok(pos) }
	}

	/// A file descriptor result that returned an error code.
	#[must_use]
	pub const fn error(error_code: u32) -> Self {
		Self {
			position: Err(error_code),
		}
	}

	/// The resulting file position.
	///
	/// ## Errors
	///
	/// If there was an error code.
	pub const fn result(&self) -> Result<u32, u32> {
		self.position
	}
}

impl From<&SataGetFilePositionResult> for Bytes {
	fn from(value: &SataGetFilePositionResult) -> Self {
		let mut response = BytesMut::with_capacity(8);

		match value.position {
			Ok(pos) => {
				response.put_u32(0);
				response.put_u32(pos);
			}
			Err(code) => {
				response.put_u32(code);
				response.put_u32(u32::from_be_bytes([0xFF, 0xFF, 0xFF, 0xFF]));
			}
		}

		response.freeze()
	}
}

impl From<SataGetFilePositionResult> for Bytes {
	fn from(value: SataGetFilePositionResult) -> Self {
		Self::from(&value)
	}
}

impl TryFrom<Bytes> for SataGetFilePositionResult {
	type Error = NetworkParseError;

	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
		if value.len() < 0x8 {
			return Err(NetworkParseError::FieldNotLongEnough(
				"SataPacket",
				"FilePositionResult",
				0x8,
				value.len(),
				value,
			));
		}
		if value.len() > 0x8 {
			return Err(NetworkParseError::UnexpectedTrailer(
				"SataFilePositionResult",
				value.slice(0x8..),
			));
		}

		let rc = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
		let pos = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);

		if rc == 0 {
			Ok(Self::success(pos))
		} else {
			Ok(Self::error(rc))
		}
	}
}

impl Display for SataGetFilePositionResult {
	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
		match self.position {
			Ok(pos) => write!(fmt, "Success ({pos})"),
			Err(rc) => write!(fmt, "Failure ({rc:02x})"),
		}
	}
}