Skip to content

Commit 166faea

Browse files
committed
make sure the error kind is properly transferred when serializing io errors.
I now go through a lot of effort assigning good error kinds and don't want them to be lost!
1 parent 845e37c commit 166faea

File tree

1 file changed

+176
-10
lines changed

1 file changed

+176
-10
lines changed

src/util.rs

Lines changed: 176 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,109 @@ pub mod serde {
1111
use std::{fmt, io};
1212

1313
use serde::{
14-
de::{self, Visitor},
14+
de::{self, SeqAccess, Visitor},
15+
ser::SerializeTuple,
1516
Deserializer, Serializer,
1617
};
1718

19+
fn error_kind_to_u8(kind: io::ErrorKind) -> u8 {
20+
match kind {
21+
io::ErrorKind::AddrInUse => 0,
22+
io::ErrorKind::AddrNotAvailable => 1,
23+
io::ErrorKind::AlreadyExists => 2,
24+
io::ErrorKind::ArgumentListTooLong => 3,
25+
io::ErrorKind::BrokenPipe => 4,
26+
io::ErrorKind::ConnectionAborted => 5,
27+
io::ErrorKind::ConnectionRefused => 6,
28+
io::ErrorKind::ConnectionReset => 7,
29+
io::ErrorKind::CrossesDevices => 8,
30+
io::ErrorKind::Deadlock => 9,
31+
io::ErrorKind::DirectoryNotEmpty => 10,
32+
io::ErrorKind::ExecutableFileBusy => 11,
33+
io::ErrorKind::FileTooLarge => 12,
34+
io::ErrorKind::HostUnreachable => 13,
35+
io::ErrorKind::Interrupted => 14,
36+
io::ErrorKind::InvalidData => 15,
37+
io::ErrorKind::InvalidFilename => 16,
38+
io::ErrorKind::InvalidInput => 17,
39+
io::ErrorKind::IsADirectory => 18,
40+
io::ErrorKind::NetworkDown => 19,
41+
io::ErrorKind::NetworkUnreachable => 20,
42+
io::ErrorKind::NotADirectory => 21,
43+
io::ErrorKind::NotConnected => 22,
44+
io::ErrorKind::NotFound => 23,
45+
io::ErrorKind::NotSeekable => 24,
46+
io::ErrorKind::Other => 25,
47+
io::ErrorKind::OutOfMemory => 26,
48+
io::ErrorKind::PermissionDenied => 27,
49+
io::ErrorKind::QuotaExceeded => 28,
50+
io::ErrorKind::ReadOnlyFilesystem => 29,
51+
io::ErrorKind::ResourceBusy => 30,
52+
io::ErrorKind::StaleNetworkFileHandle => 31,
53+
io::ErrorKind::StorageFull => 32,
54+
io::ErrorKind::TimedOut => 33,
55+
io::ErrorKind::TooManyLinks => 34,
56+
io::ErrorKind::UnexpectedEof => 35,
57+
io::ErrorKind::Unsupported => 36,
58+
io::ErrorKind::WouldBlock => 37,
59+
io::ErrorKind::WriteZero => 38,
60+
_ => 25,
61+
}
62+
}
63+
64+
fn u8_to_error_kind(num: u8) -> io::ErrorKind {
65+
match num {
66+
0 => io::ErrorKind::AddrInUse,
67+
1 => io::ErrorKind::AddrNotAvailable,
68+
2 => io::ErrorKind::AlreadyExists,
69+
3 => io::ErrorKind::ArgumentListTooLong,
70+
4 => io::ErrorKind::BrokenPipe,
71+
5 => io::ErrorKind::ConnectionAborted,
72+
6 => io::ErrorKind::ConnectionRefused,
73+
7 => io::ErrorKind::ConnectionReset,
74+
8 => io::ErrorKind::CrossesDevices,
75+
9 => io::ErrorKind::Deadlock,
76+
10 => io::ErrorKind::DirectoryNotEmpty,
77+
11 => io::ErrorKind::ExecutableFileBusy,
78+
12 => io::ErrorKind::FileTooLarge,
79+
13 => io::ErrorKind::HostUnreachable,
80+
14 => io::ErrorKind::Interrupted,
81+
15 => io::ErrorKind::InvalidData,
82+
16 => io::ErrorKind::InvalidFilename,
83+
17 => io::ErrorKind::InvalidInput,
84+
18 => io::ErrorKind::IsADirectory,
85+
19 => io::ErrorKind::NetworkDown,
86+
20 => io::ErrorKind::NetworkUnreachable,
87+
21 => io::ErrorKind::NotADirectory,
88+
22 => io::ErrorKind::NotConnected,
89+
23 => io::ErrorKind::NotFound,
90+
24 => io::ErrorKind::NotSeekable,
91+
25 => io::ErrorKind::Other,
92+
26 => io::ErrorKind::OutOfMemory,
93+
27 => io::ErrorKind::PermissionDenied,
94+
28 => io::ErrorKind::QuotaExceeded,
95+
29 => io::ErrorKind::ReadOnlyFilesystem,
96+
30 => io::ErrorKind::ResourceBusy,
97+
31 => io::ErrorKind::StaleNetworkFileHandle,
98+
32 => io::ErrorKind::StorageFull,
99+
33 => io::ErrorKind::TimedOut,
100+
34 => io::ErrorKind::TooManyLinks,
101+
35 => io::ErrorKind::UnexpectedEof,
102+
36 => io::ErrorKind::Unsupported,
103+
37 => io::ErrorKind::WouldBlock,
104+
38 => io::ErrorKind::WriteZero,
105+
_ => io::ErrorKind::Other,
106+
}
107+
}
108+
18109
pub fn serialize<S>(error: &io::Error, serializer: S) -> Result<S::Ok, S::Error>
19110
where
20111
S: Serializer,
21112
{
22-
// Serialize the error kind and message
23-
serializer.serialize_str(&format!("{:?}:{}", error.kind(), error))
113+
let mut tup = serializer.serialize_tuple(2)?;
114+
tup.serialize_element(&error_kind_to_u8(error.kind()))?;
115+
tup.serialize_element(&error.to_string())?;
116+
tup.end()
24117
}
25118

26119
pub fn deserialize<'de, D>(deserializer: D) -> Result<io::Error, D::Error>
@@ -33,20 +126,93 @@ pub mod serde {
33126
type Value = io::Error;
34127

35128
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
36-
formatter.write_str("an io::Error string representation")
129+
formatter.write_str("a tuple of (u32, String) representing io::Error")
37130
}
38131

39-
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
132+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
40133
where
41-
E: de::Error,
134+
A: SeqAccess<'de>,
42135
{
43-
// For simplicity, create a generic error
44-
// In a real app, you might want to parse the kind from the string
45-
Ok(io::Error::other(value))
136+
let num: u8 = seq
137+
.next_element()?
138+
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
139+
let message: String = seq
140+
.next_element()?
141+
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
142+
let kind = u8_to_error_kind(num);
143+
Ok(io::Error::new(kind, message))
46144
}
47145
}
48146

49-
deserializer.deserialize_str(IoErrorVisitor)
147+
deserializer.deserialize_tuple(2, IoErrorVisitor)
148+
}
149+
}
150+
151+
#[cfg(test)]
152+
mod tests {
153+
use std::io::{self, ErrorKind};
154+
155+
use postcard;
156+
use serde::{Deserialize, Serialize};
157+
158+
use super::io_error_serde;
159+
160+
#[derive(Serialize, Deserialize)]
161+
struct TestError(#[serde(with = "io_error_serde")] io::Error);
162+
163+
#[test]
164+
fn test_roundtrip_error_kinds() {
165+
let message = "test error";
166+
let kinds = [
167+
ErrorKind::AddrInUse,
168+
ErrorKind::AddrNotAvailable,
169+
ErrorKind::AlreadyExists,
170+
ErrorKind::ArgumentListTooLong,
171+
ErrorKind::BrokenPipe,
172+
ErrorKind::ConnectionAborted,
173+
ErrorKind::ConnectionRefused,
174+
ErrorKind::ConnectionReset,
175+
ErrorKind::CrossesDevices,
176+
ErrorKind::Deadlock,
177+
ErrorKind::DirectoryNotEmpty,
178+
ErrorKind::ExecutableFileBusy,
179+
ErrorKind::FileTooLarge,
180+
ErrorKind::HostUnreachable,
181+
ErrorKind::Interrupted,
182+
ErrorKind::InvalidData,
183+
ErrorKind::InvalidFilename,
184+
ErrorKind::InvalidInput,
185+
ErrorKind::IsADirectory,
186+
ErrorKind::NetworkDown,
187+
ErrorKind::NetworkUnreachable,
188+
ErrorKind::NotADirectory,
189+
ErrorKind::NotConnected,
190+
ErrorKind::NotFound,
191+
ErrorKind::NotSeekable,
192+
ErrorKind::Other,
193+
ErrorKind::OutOfMemory,
194+
ErrorKind::PermissionDenied,
195+
ErrorKind::QuotaExceeded,
196+
ErrorKind::ReadOnlyFilesystem,
197+
ErrorKind::ResourceBusy,
198+
ErrorKind::StaleNetworkFileHandle,
199+
ErrorKind::StorageFull,
200+
ErrorKind::TimedOut,
201+
ErrorKind::TooManyLinks,
202+
ErrorKind::UnexpectedEof,
203+
ErrorKind::Unsupported,
204+
ErrorKind::WouldBlock,
205+
ErrorKind::WriteZero,
206+
];
207+
208+
for kind in kinds {
209+
let err = TestError(io::Error::new(kind, message));
210+
let serialized = postcard::to_allocvec(&err).unwrap();
211+
let deserialized: TestError = postcard::from_bytes(&serialized).unwrap();
212+
213+
assert_eq!(err.0.kind(), deserialized.0.kind());
214+
assert_eq!(err.0.to_string(), deserialized.0.to_string());
215+
}
50216
}
51217
}
52218
}

0 commit comments

Comments
 (0)