cat_dev/fsemul/pcfs/sata/proto/
rename.rs

1//! Definitions for the `Rename` packet type, and it's response types.
2//!
3//! This moves a file from one place to another as necessary.
4
5use crate::{errors::NetworkParseError, fsemul::pcfs::errors::PcfsApiError};
6use bytes::{Bytes, BytesMut};
7use std::ffi::CStr;
8use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
9
10/// A packet to rename an arbitrary path.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct SataRenamePacketBody {
13	/// The source path which will be renamed.
14	source_path: String,
15	/// The path, aka where the final path will be.
16	dest_path: String,
17}
18
19impl SataRenamePacketBody {
20	/// Attempt to construct a new rename packet body.
21	///
22	/// ## Errors
23	///
24	/// If any path is longer than 511 bytes. Normally the max path is 512 bytes,
25	/// but because we need to encode our data as a C-String with a NUL
26	/// terminator we cannot be longer than 511 bytes.
27	///
28	/// Consider using relative/mapped paths if possible when dealing with long
29	/// paths.
30	pub fn new(source_path: String, dest_path: String) -> Result<Self, PcfsApiError> {
31		if source_path.len() > 511 {
32			return Err(PcfsApiError::PathTooLong(source_path));
33		}
34		if dest_path.len() > 511 {
35			return Err(PcfsApiError::PathTooLong(dest_path));
36		}
37
38		Ok(Self {
39			source_path,
40			dest_path,
41		})
42	}
43
44	#[must_use]
45	pub fn source_path(&self) -> &str {
46		self.source_path.as_str()
47	}
48
49	#[must_use]
50	pub fn dest_path(&self) -> &str {
51		self.dest_path.as_str()
52	}
53
54	/// Update the source path to send in this rename packet directory.
55	///
56	/// ## Errors
57	///
58	/// If the path is longer than 511 bytes. Normally the max path is 512 bytes,
59	/// but because we need to encode our data as a C-String with a NUL
60	/// terminator we cannot be longer than 511 bytes.
61	///
62	/// Consider using relative/mapped paths if possible when dealing with long
63	/// paths.
64	pub fn set_source_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
65		if new_path.len() > 511 {
66			return Err(PcfsApiError::PathTooLong(new_path));
67		}
68
69		self.source_path = new_path;
70		Ok(())
71	}
72
73	/// Update the destination path to send in this rename packet directory.
74	///
75	/// ## Errors
76	///
77	/// If the path is longer than 511 bytes. Normally the max path is 512 bytes,
78	/// but because we need to encode our data as a C-String with a NUL
79	/// terminator we cannot be longer than 511 bytes.
80	///
81	/// Consider using relative/mapped paths if possible when dealing with long
82	/// paths.
83	pub fn set_dest_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
84		if new_path.len() > 511 {
85			return Err(PcfsApiError::PathTooLong(new_path));
86		}
87
88		self.dest_path = new_path;
89		Ok(())
90	}
91}
92
93impl TryFrom<Bytes> for SataRenamePacketBody {
94	type Error = NetworkParseError;
95
96	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
97		if value.len() < 0x400 {
98			return Err(NetworkParseError::FieldNotLongEnough(
99				"SataRenamePacketBody",
100				"Body",
101				0x400,
102				value.len(),
103				value,
104			));
105		}
106		if value.len() > 0x400 {
107			return Err(NetworkParseError::UnexpectedTrailer(
108				"SataRenamePacketBody",
109				value.slice(0x400..),
110			));
111		}
112
113		let source_path_c_str =
114			CStr::from_bytes_until_nul(&value[..0x200]).map_err(NetworkParseError::BadCString)?;
115		let dest_path_c_str =
116			CStr::from_bytes_until_nul(&value[0x200..]).map_err(NetworkParseError::BadCString)?;
117
118		Ok(Self {
119			source_path: source_path_c_str.to_str()?.to_owned(),
120			dest_path: dest_path_c_str.to_str()?.to_owned(),
121		})
122	}
123}
124
125impl From<&SataRenamePacketBody> for Bytes {
126	fn from(value: &SataRenamePacketBody) -> Self {
127		let mut result = BytesMut::with_capacity(0x400);
128		result.extend_from_slice(value.source_path.as_bytes());
129		// These are C Strings so we need a NUL terminator.
130		// Pad with `0`, til we get a full path with a nul terminator.
131		result.extend(BytesMut::zeroed(0x200 - result.len()));
132		result.extend_from_slice(value.dest_path.as_bytes());
133		// Pad again....
134		result.extend(BytesMut::zeroed(0x400 - result.len()));
135		result.freeze()
136	}
137}
138
139impl From<SataRenamePacketBody> for Bytes {
140	fn from(value: SataRenamePacketBody) -> Self {
141		Self::from(&value)
142	}
143}
144
145const SATA_RENAME_PACKET_BODY_FIELDS: &[NamedField<'static>] =
146	&[NamedField::new("source_path"), NamedField::new("dest_path")];
147
148impl Structable for SataRenamePacketBody {
149	fn definition(&self) -> StructDef<'_> {
150		StructDef::new_static(
151			"SataRenamePacketBody",
152			Fields::Named(SATA_RENAME_PACKET_BODY_FIELDS),
153		)
154	}
155}
156
157impl Valuable for SataRenamePacketBody {
158	fn as_value(&self) -> Value<'_> {
159		Value::Structable(self)
160	}
161
162	fn visit(&self, visitor: &mut dyn Visit) {
163		visitor.visit_named_fields(&NamedValues::new(
164			SATA_RENAME_PACKET_BODY_FIELDS,
165			&[
166				Valuable::as_value(&self.source_path),
167				Valuable::as_value(&self.dest_path),
168			],
169		));
170	}
171}