use crate::{
errors::NetworkParseError,
fsemul::pcfs::errors::{PcfsApiError, SataProtocolError},
};
use bytes::{Bytes, BytesMut};
use std::ffi::CStr;
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SataOpenFilePacketBody {
mode_string: String,
path: String,
}
impl SataOpenFilePacketBody {
pub fn new(path: String, mode_string: String) -> Result<Self, PcfsApiError> {
if path.len() > 511 {
return Err(PcfsApiError::PathTooLong(path));
}
for (idx, car) in mode_string.chars().enumerate() {
if idx == 0 && !['r', 'w', 'a'].contains(&car) {
return Err(PcfsApiError::BadModeString(mode_string));
}
if idx > 2 {
return Err(PcfsApiError::BadModeString(mode_string));
}
if idx != 0 && !['b', '+'].contains(&car) {
return Err(PcfsApiError::BadModeString(mode_string));
}
}
Ok(Self { mode_string, path })
}
#[must_use]
pub fn mode(&self) -> &str {
self.mode_string.as_str()
}
pub fn set_mode(&mut self, new_mode: String) -> Result<(), PcfsApiError> {
for (idx, car) in new_mode.chars().enumerate() {
if idx == 0 && !['r', 'w', 'a'].contains(&car) {
return Err(PcfsApiError::BadModeString(new_mode));
}
if idx > 2 {
return Err(PcfsApiError::BadModeString(new_mode));
}
if idx != 0 && !['b', '+'].contains(&car) {
return Err(PcfsApiError::BadModeString(new_mode));
}
}
self.mode_string = new_mode;
Ok(())
}
#[must_use]
pub fn path(&self) -> &str {
self.path.as_str()
}
pub fn set_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
if new_path.len() > 511 {
return Err(PcfsApiError::PathTooLong(new_path));
}
self.path = new_path;
Ok(())
}
}
impl From<&SataOpenFilePacketBody> for Bytes {
fn from(value: &SataOpenFilePacketBody) -> Self {
let mut result = BytesMut::with_capacity(0x210);
result.extend_from_slice(value.mode_string.as_bytes());
result.extend(BytesMut::with_capacity(0x10 - result.len()));
result.extend_from_slice(value.path.as_bytes());
result.extend(BytesMut::zeroed(0x210 - result.len()));
result.freeze()
}
}
impl From<SataOpenFilePacketBody> for Bytes {
fn from(value: SataOpenFilePacketBody) -> Self {
Self::from(&value)
}
}
impl TryFrom<Bytes> for SataOpenFilePacketBody {
type Error = NetworkParseError;
fn try_from(value: Bytes) -> Result<Self, Self::Error> {
if value.len() < 0x210 {
return Err(NetworkParseError::FieldNotLongEnough(
"SataOpenFile",
"Body",
0x210,
value.len(),
value,
));
}
if value.len() > 0x210 {
return Err(NetworkParseError::UnexpectedTrailer(
"SataOpenFile",
value.slice(0x210..),
));
}
let (mode_bytes, path_bytes) = value.split_at(0x10);
let mode_c_str =
CStr::from_bytes_until_nul(mode_bytes).map_err(NetworkParseError::BadCString)?;
let path_c_str =
CStr::from_bytes_until_nul(path_bytes).map_err(NetworkParseError::BadCString)?;
let final_mode = mode_c_str.to_str()?.to_owned();
for (idx, car) in final_mode.chars().enumerate() {
if idx == 0 && !['r', 'w', 'a'].contains(&car) {
return Err(SataProtocolError::BadModeString(final_mode).into());
}
if idx > 2 {
return Err(SataProtocolError::BadModeString(final_mode).into());
}
if idx != 0 && !['b', '+'].contains(&car) {
return Err(SataProtocolError::BadModeString(final_mode).into());
}
}
Ok(Self {
mode_string: final_mode,
path: path_c_str.to_str()?.to_owned(),
})
}
}
const SATA_OPEN_FILE_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("path")];
impl Structable for SataOpenFilePacketBody {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static(
"SataOpenFilePacketBody",
Fields::Named(SATA_OPEN_FILE_PACKET_BODY_FIELDS),
)
}
}
impl Valuable for SataOpenFilePacketBody {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
SATA_OPEN_FILE_PACKET_BODY_FIELDS,
&[Valuable::as_value(&self.path)],
));
}
}