1use std::{
2 any::type_name,
3 fmt::Display,
4 io::{Read, Seek, Write},
5};
6
7#[derive(Debug)]
8pub struct StreamError {
9 pub error: std::io::Error,
10 pub pos: u64,
11 pub context: Vec<String>,
12}
13
14impl StreamError {
15 pub fn new_context(error: std::io::Error, pos: u64, context: Vec<String>) -> Self {
16 Self {
17 error,
18 pos,
19 context,
20 }
21 }
22
23 pub fn new(error: std::io::Error, pos: u64) -> Self {
24 Self {
25 error,
26 pos,
27 context: Vec::new(),
28 }
29 }
30
31 pub fn add_context<C: ToString>(self, new_context: C) -> Self {
32 let mut context = self.context;
33 context.push(new_context.to_string());
34 Self::new_context(self.error, self.pos, context)
35 }
36
37 pub fn new_string_context<E: ToString, C: ToString>(error: E, pos: u64, context: C) -> Self {
38 Self {
39 error: std::io::Error::other(error.to_string()),
40 pos,
41 context: vec![context.to_string()],
42 }
43 }
44}
45
46impl Display for StreamError {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 let string = format!("error: {} at {} with context:", self.error, self.pos);
49
50 let mut context_str = String::new();
51
52 for ctx in &self.context {
53 context_str.push_str(&format!("\n{ctx}"));
54 }
55
56 write!(f, "{string}\n{context_str}")
57 }
58}
59
60pub type StreamResult<T> = Result<T, StreamError>;
61
62pub trait NewResultCtx {
63 fn add_context<C: ToString, F: FnOnce() -> C>(self, context: F) -> Self;
64}
65
66impl<T> NewResultCtx for StreamResult<T> {
67 fn add_context<C: ToString, F: FnOnce() -> C>(self, context: F) -> Self {
68 match self {
69 Ok(v) => Ok(v),
70 Err(e) => Err(e.add_context(context())),
71 }
72 }
73}
74
75pub trait Readable: Sized {
76 type Args;
77 fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self>;
78}
79
80pub trait Writeable: Sized {
81 type Args;
82 fn write<W: Write + Seek>(self, writer: &mut W, args: Self::Args) -> StreamResult<()>;
83}
84
85pub trait ResultCtx: Sized {
86 type OkT;
87 fn with_context<C: ToString, F: FnOnce() -> C>(
88 self,
89 pos: u64,
90 context: F,
91 ) -> StreamResult<Self::OkT>;
92 fn stream_context<C: ToString, F: FnOnce() -> C>(self, context: F) -> StreamResult<Self::OkT>;
93 fn with_pos(self, pos: u64) -> StreamResult<Self::OkT>;
94}
95
96impl From<std::io::Error> for StreamError {
97 fn from(value: std::io::Error) -> Self {
98 Self::new(value, u64::MAX)
99 }
100}
101
102impl<T> ResultCtx for Result<T, std::io::Error> {
103 type OkT = T;
104 fn with_context<C: ToString, F: FnOnce() -> C>(
105 self,
106 pos: u64,
107 context: F,
108 ) -> StreamResult<<Self as ResultCtx>::OkT> {
109 match self {
110 Ok(v) => StreamResult::Ok(v),
111 Err(e) => StreamResult::Err(StreamError::new_context(
112 e,
113 pos,
114 vec![context().to_string()],
115 )),
116 }
117 }
118 fn with_pos(self, pos: u64) -> StreamResult<Self::OkT> {
119 match self {
120 Ok(v) => StreamResult::Ok(v),
121 Err(e) => StreamResult::Err(StreamError::new(e, pos)),
122 }
123 }
124 fn stream_context<C: ToString, F: FnOnce() -> C>(self, context: F) -> StreamResult<Self::OkT> {
125 self.with_context(u64::MAX, context)
126 }
127}
128
129fn read_data<R: Read + Seek, const N: usize>(reader: &mut R) -> StreamResult<[u8; N]> {
130 let mut buf = [0; N];
131 reader
132 .read_exact(&mut buf)
133 .with_context(reader.stream_position()?, || format!("read_data<{N}>"))?;
134 Ok(buf)
135}
136
137fn write_data<W: Write + Seek>(data: &[u8], writer: &mut W) -> StreamResult<()> {
138 writer
139 .write_all(data)
140 .with_context(writer.stream_position()?, || "write_data")
141}
142
143pub trait ReadableNoOptions: Sized {
144 fn read_no_opts<R: Read + Seek>(reader: &mut R) -> StreamResult<Self>;
145}
146
147pub trait WriteableNoOptions {
148 fn write_no_opts<W: Write + Seek>(self, writer: &mut W) -> StreamResult<()>;
149}
150
151impl<T: Readable<Args = ()>> ReadableNoOptions for T {
152 fn read_no_opts<R: Read + Seek>(reader: &mut R) -> StreamResult<Self> {
153 T::read(reader, ())
154 }
155}
156
157impl<T: Writeable<Args = ()>> WriteableNoOptions for T {
158 fn write_no_opts<W: Write + Seek>(self, writer: &mut W) -> StreamResult<()> {
159 self.write(writer, ())
160 }
161}
162
163impl Readable for u8 {
164 type Args = ();
165 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
166 Ok(Self::from_le_bytes(
167 read_data(reader).add_context(|| "read u8")?,
168 ))
169 }
170}
171
172impl Readable for u16 {
173 type Args = ();
174 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
175 Ok(Self::from_le_bytes(
176 read_data(reader).add_context(|| "read u16")?,
177 ))
178 }
179}
180
181impl Readable for u32 {
182 type Args = ();
183 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
184 Ok(Self::from_le_bytes(
185 read_data(reader).add_context(|| "read u32")?,
186 ))
187 }
188}
189
190impl Readable for u64 {
191 type Args = ();
192 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
193 Ok(Self::from_le_bytes(
194 read_data(reader).add_context(|| "read u64")?,
195 ))
196 }
197}
198
199impl Readable for f32 {
200 type Args = ();
201 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
202 Ok(Self::from_le_bytes(
203 read_data(reader).add_context(|| "read f32")?,
204 ))
205 }
206}
207impl Readable for f64 {
208 type Args = ();
209 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
210 Ok(Self::from_le_bytes(
211 read_data(reader).add_context(|| "read f64")?,
212 ))
213 }
214}
215
216impl Writeable for u8 {
217 type Args = ();
218 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
219 write_data(&self.to_le_bytes(), writer).add_context(|| "write u8")
220 }
221}
222
223impl Writeable for u16 {
224 type Args = ();
225 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
226 write_data(&self.to_le_bytes(), writer).add_context(|| "write u16")
227 }
228}
229
230impl Writeable for u32 {
231 type Args = ();
232 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
233 write_data(&self.to_le_bytes(), writer).add_context(|| "write u32")
234 }
235}
236impl Writeable for u64 {
237 type Args = ();
238 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
239 write_data(&self.to_le_bytes(), writer).add_context(|| "write u64")
240 }
241}
242impl Writeable for f32 {
243 type Args = ();
244 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
245 write_data(&self.to_le_bytes(), writer).add_context(|| "write f32")
246 }
247}
248impl Writeable for f64 {
249 type Args = ();
250 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
251 write_data(&self.to_le_bytes(), writer).add_context(|| "write f64")
252 }
253}
254
255pub trait VecReadable: Sized {
256 fn read_vec<R: Read + Seek>(reader: &mut R, count: usize) -> StreamResult<Self>;
257}
258
259pub trait VecWritable {
260 fn write_vec<W: Write + Seek>(self, writer: &mut W) -> StreamResult<()>;
261}
262
263impl<T: ReadableNoOptions> VecReadable for Vec<T> {
264 fn read_vec<R: Read + Seek>(reader: &mut R, count: usize) -> StreamResult<Self> {
265 let mut data = Vec::with_capacity(count);
266
267 for i in 0..count {
268 data.push(T::read_no_opts(reader).add_context(|| {
269 format!("reading item {} of vec of type {}", i, type_name::<T>())
270 })?);
271 }
272
273 Ok(data)
274 }
275}
276
277impl<T: WriteableNoOptions> VecWritable for Vec<T> {
278 fn write_vec<W: Write + Seek>(self, writer: &mut W) -> StreamResult<()> {
279 for (i, val) in self.into_iter().enumerate() {
280 val.write_no_opts(writer).add_context(|| {
281 format!("writing item {} of vec of type {}", i, type_name::<T>())
282 })?;
283 }
284
285 Ok(())
286 }
287}
288
289impl<T: ReadableNoOptions, const N: usize> Readable for [T; N] {
290 type Args = ();
291 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
292 let mut data = Vec::with_capacity(N);
293 for i in 0..N {
294 let val = T::read_no_opts(reader).add_context(|| {
295 format!("reading element {i} of type [{}; {N}] ", type_name::<T>())
296 })?;
297
298 data.push(val);
299 }
300
301 Ok(data.try_into().unwrap_or_else(|_| unreachable!()))
302 }
303}
304
305impl<T: WriteableNoOptions, const N: usize> Writeable for [T; N] {
306 type Args = ();
307 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
308 for (i, val) in self.into_iter().enumerate() {
309 val.write_no_opts(writer)
310 .add_context(|| format!("writing element {i} of [{}; {N}]", type_name::<T>()))?;
311 }
312
313 Ok(())
314 }
315}
316
317impl Readable for bool {
318 type Args = ();
319 fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
320 let data: u8 = u8::read_no_opts(reader).add_context(|| "read u8 for bool")?;
321
322 Ok(data != 0)
323 }
324}
325
326impl Writeable for bool {
327 type Args = ();
328 fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
329 let data: u8 = match self {
330 true => 1,
331 false => 0,
332 };
333 data.write_no_opts(writer)
334 .add_context(|| "write u8 for bool")
335 }
336}