cat_dev/fsemul/pcfs/sata/proto/
open_file.rs1use crate::{
7 errors::NetworkParseError,
8 fsemul::pcfs::errors::{PcfsApiError, SataProtocolError},
9};
10use bytes::{Bytes, BytesMut};
11use std::ffi::CStr;
12use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct SataOpenFilePacketBody {
17 mode_string: String,
19 path: String,
30}
31
32impl SataOpenFilePacketBody {
33 pub fn new(path: String, mode_string: String) -> Result<Self, PcfsApiError> {
48 if path.len() > 511 {
49 return Err(PcfsApiError::PathTooLong(path));
50 }
51 for (idx, car) in mode_string.chars().enumerate() {
52 if idx == 0 && !['r', 'w', 'a'].contains(&car) {
53 return Err(PcfsApiError::BadModeString(mode_string));
54 }
55 if idx > 2 {
56 return Err(PcfsApiError::BadModeString(mode_string));
57 }
58 if idx != 0 && !['b', '+'].contains(&car) {
59 return Err(PcfsApiError::BadModeString(mode_string));
60 }
61 }
62
63 Ok(Self { mode_string, path })
64 }
65
66 #[must_use]
67 pub fn mode(&self) -> &str {
68 self.mode_string.as_str()
69 }
70
71 pub fn set_mode(&mut self, new_mode: String) -> Result<(), PcfsApiError> {
79 for (idx, car) in new_mode.chars().enumerate() {
80 if idx == 0 && !['r', 'w', 'a'].contains(&car) {
81 return Err(PcfsApiError::BadModeString(new_mode));
82 }
83 if idx > 2 {
84 return Err(PcfsApiError::BadModeString(new_mode));
85 }
86 if idx != 0 && !['b', '+'].contains(&car) {
87 return Err(PcfsApiError::BadModeString(new_mode));
88 }
89 }
90
91 self.mode_string = new_mode;
92 Ok(())
93 }
94
95 #[must_use]
96 pub fn path(&self) -> &str {
97 self.path.as_str()
98 }
99
100 pub fn set_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
111 if new_path.len() > 511 {
112 return Err(PcfsApiError::PathTooLong(new_path));
113 }
114
115 self.path = new_path;
116 Ok(())
117 }
118}
119
120impl From<&SataOpenFilePacketBody> for Bytes {
121 fn from(value: &SataOpenFilePacketBody) -> Self {
122 let mut result = BytesMut::with_capacity(0x210);
123 result.extend_from_slice(value.mode_string.as_bytes());
124 result.extend(BytesMut::with_capacity(0x10 - result.len()));
130 result.extend_from_slice(value.path.as_bytes());
131 result.extend(BytesMut::zeroed(0x210 - result.len()));
134 result.freeze()
135 }
136}
137
138impl From<SataOpenFilePacketBody> for Bytes {
139 fn from(value: SataOpenFilePacketBody) -> Self {
140 Self::from(&value)
141 }
142}
143
144impl TryFrom<Bytes> for SataOpenFilePacketBody {
145 type Error = NetworkParseError;
146
147 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
148 if value.len() < 0x210 {
149 return Err(NetworkParseError::FieldNotLongEnough(
150 "SataOpenFile",
151 "Body",
152 0x210,
153 value.len(),
154 value,
155 ));
156 }
157 if value.len() > 0x210 {
158 return Err(NetworkParseError::UnexpectedTrailer(
159 "SataOpenFile",
160 value.slice(0x210..),
161 ));
162 }
163
164 let (mode_bytes, path_bytes) = value.split_at(0x10);
165 let mode_c_str =
166 CStr::from_bytes_until_nul(mode_bytes).map_err(NetworkParseError::BadCString)?;
167 let path_c_str =
168 CStr::from_bytes_until_nul(path_bytes).map_err(NetworkParseError::BadCString)?;
169 let final_mode = mode_c_str.to_str()?.to_owned();
170 for (idx, car) in final_mode.chars().enumerate() {
171 if idx == 0 && !['r', 'w', 'a'].contains(&car) {
172 return Err(SataProtocolError::BadModeString(final_mode).into());
173 }
174 if idx > 2 {
175 return Err(SataProtocolError::BadModeString(final_mode).into());
176 }
177 if idx != 0 && !['b', '+'].contains(&car) {
178 return Err(SataProtocolError::BadModeString(final_mode).into());
179 }
180 }
181
182 Ok(Self {
183 mode_string: final_mode,
184 path: path_c_str.to_str()?.to_owned(),
185 })
186 }
187}
188
189const SATA_OPEN_FILE_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("path")];
190
191impl Structable for SataOpenFilePacketBody {
192 fn definition(&self) -> StructDef<'_> {
193 StructDef::new_static(
194 "SataOpenFilePacketBody",
195 Fields::Named(SATA_OPEN_FILE_PACKET_BODY_FIELDS),
196 )
197 }
198}
199
200impl Valuable for SataOpenFilePacketBody {
201 fn as_value(&self) -> Value<'_> {
202 Value::Structable(self)
203 }
204
205 fn visit(&self, visitor: &mut dyn Visit) {
206 visitor.visit_named_fields(&NamedValues::new(
207 SATA_OPEN_FILE_PACKET_BODY_FIELDS,
208 &[Valuable::as_value(&self.path)],
209 ));
210 }
211}