1 | // Copyright 2024 The Fuchsia Authors |
2 | // |
3 | // Licensed under the 2-Clause BSD License <LICENSE-BSD or |
4 | // https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0 |
5 | // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT |
6 | // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. |
7 | // This file may not be copied, modified, or distributed except according to |
8 | // those terms. |
9 | |
10 | //! Types related to error reporting. |
11 | //! |
12 | //! ## Single failure mode errors |
13 | //! |
14 | //! Generally speaking, zerocopy's conversions may fail for one of up to three |
15 | //! reasons: |
16 | //! - [`AlignmentError`]: the conversion source was improperly aligned |
17 | //! - [`SizeError`]: the conversion source was of incorrect size |
18 | //! - [`ValidityError`]: the conversion source contained invalid data |
19 | //! |
20 | //! Methods that only have one failure mode, like |
21 | //! [`FromBytes::read_from_bytes`], return that mode's corresponding error type |
22 | //! directly. |
23 | //! |
24 | //! ## Compound errors |
25 | //! |
26 | //! Conversion methods that have either two or three possible failure modes |
27 | //! return one of these error types: |
28 | //! - [`CastError`]: the error type of reference conversions |
29 | //! - [`TryCastError`]: the error type of fallible reference conversions |
30 | //! - [`TryReadError`]: the error type of fallible read conversions |
31 | //! |
32 | //! ## [`Unaligned`] destination types |
33 | //! |
34 | //! For [`Unaligned`] destination types, alignment errors are impossible. All |
35 | //! compound error types support infallibly discarding the alignment error via |
36 | //! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as |
37 | //! From<ConvertError>>::from`][size-error-from]. |
38 | //! |
39 | //! [size-error-from]: struct.SizeError.html#method.from-1 |
40 | //! |
41 | //! ## Accessing the conversion source |
42 | //! |
43 | //! All error types provide an `into_src` method that converts the error into |
44 | //! the source value underlying the failed conversion. |
45 | //! |
46 | //! ## Display formatting |
47 | //! |
48 | //! All error types provide a `Display` implementation that produces a |
49 | //! human-readable error message. When `debug_assertions` are enabled, these |
50 | //! error messages are verbose and may include potentially sensitive |
51 | //! information, including: |
52 | //! |
53 | //! - the names of the involved types |
54 | //! - the sizes of the involved types |
55 | //! - the addresses of the involved types |
56 | //! - the contents of the involved types |
57 | //! |
58 | //! When `debug_assertions` are disabled (as is default for `release` builds), |
59 | //! such potentially sensitive information is excluded. |
60 | //! |
61 | //! In the future, we may support manually configuring this behavior. If you are |
62 | //! interested in this feature, [let us know on GitHub][issue-1457] so we know |
63 | //! to prioritize it. |
64 | //! |
65 | //! [issue-1457]: https://github.com/google/zerocopy/issues/1457 |
66 | //! |
67 | //! ## Validation order |
68 | //! |
69 | //! Our conversion methods typically check alignment, then size, then bit |
70 | //! validity. However, we do not guarantee that this is always the case, and |
71 | //! this behavior may change between releases. |
72 | //! |
73 | //! ## `Send`, `Sync`, and `'static` |
74 | //! |
75 | //! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter |
76 | //! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an |
77 | //! error is sent or synchronized across threads; e.g.: |
78 | //! |
79 | //! ```compile_fail,E0515 |
80 | //! use zerocopy::*; |
81 | //! |
82 | //! let result: SizeError<&[u8], u32> = std::thread::spawn(|| { |
83 | //! let source = &mut [0u8, 1, 2][..]; |
84 | //! // Try (and fail) to read a `u32` from `source`. |
85 | //! u32::read_from_bytes(source).unwrap_err() |
86 | //! }).join().unwrap(); |
87 | //! ``` |
88 | //! |
89 | //! To work around this, use [`map_src`][CastError::map_src] to convert the |
90 | //! source parameter to an unproblematic type; e.g.: |
91 | //! |
92 | //! ``` |
93 | //! use zerocopy::*; |
94 | //! |
95 | //! let result: SizeError<(), u32> = std::thread::spawn(|| { |
96 | //! let source = &mut [0u8, 1, 2][..]; |
97 | //! // Try (and fail) to read a `u32` from `source`. |
98 | //! u32::read_from_bytes(source).unwrap_err() |
99 | //! // Erase the error source. |
100 | //! .map_src(drop) |
101 | //! }).join().unwrap(); |
102 | //! ``` |
103 | //! |
104 | //! Alternatively, use `.to_string()` to eagerly convert the error into a |
105 | //! human-readable message; e.g.: |
106 | //! |
107 | //! ``` |
108 | //! use zerocopy::*; |
109 | //! |
110 | //! let result: Result<u32, String> = std::thread::spawn(|| { |
111 | //! let source = &mut [0u8, 1, 2][..]; |
112 | //! // Try (and fail) to read a `u32` from `source`. |
113 | //! u32::read_from_bytes(source) |
114 | //! // Eagerly render the error message. |
115 | //! .map_err(|err| err.to_string()) |
116 | //! }).join().unwrap(); |
117 | //! ``` |
118 | use core::{ |
119 | convert::Infallible, |
120 | fmt::{self, Debug, Write}, |
121 | ops::Deref, |
122 | }; |
123 | |
124 | #[cfg (zerocopy_core_error_1_81_0)] |
125 | use core::error::Error; |
126 | #[cfg (all(not(zerocopy_core_error_1_81_0), any(feature = "std" , test)))] |
127 | use std::error::Error; |
128 | |
129 | use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned}; |
130 | #[cfg (doc)] |
131 | use crate::{FromBytes, Ref}; |
132 | |
133 | /// Zerocopy's generic error type. |
134 | /// |
135 | /// Generally speaking, zerocopy's conversions may fail for one of up to three |
136 | /// reasons: |
137 | /// - [`AlignmentError`]: the conversion source was improperly aligned |
138 | /// - [`SizeError`]: the conversion source was of incorrect size |
139 | /// - [`ValidityError`]: the conversion source contained invalid data |
140 | /// |
141 | /// However, not all conversions produce all errors. For instance, |
142 | /// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but |
143 | /// not validity issues. This generic error type captures these |
144 | /// (im)possibilities via parameterization: `A` is parameterized with |
145 | /// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is |
146 | /// parameterized with [`Infallible`]. |
147 | /// |
148 | /// Zerocopy never uses this type directly in its API. Rather, we provide three |
149 | /// pre-parameterized aliases: |
150 | /// - [`CastError`]: the error type of reference conversions |
151 | /// - [`TryCastError`]: the error type of fallible reference conversions |
152 | /// - [`TryReadError`]: the error type of fallible read conversions |
153 | #[derive (PartialEq, Eq)] |
154 | pub enum ConvertError<A, S, V> { |
155 | /// The conversion source was improperly aligned. |
156 | Alignment(A), |
157 | /// The conversion source was of incorrect size. |
158 | Size(S), |
159 | /// The conversion source contained invalid data. |
160 | Validity(V), |
161 | } |
162 | |
163 | impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>> |
164 | for ConvertError<Infallible, S, V> |
165 | { |
166 | /// Infallibly discards the alignment error from this `ConvertError` since |
167 | /// `Dst` is unaligned. |
168 | /// |
169 | /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment |
170 | /// error. This method permits discarding that alignment error infallibly |
171 | /// and replacing it with [`Infallible`]. |
172 | /// |
173 | /// [`Dst: Unaligned`]: crate::Unaligned |
174 | /// |
175 | /// # Examples |
176 | /// |
177 | /// ``` |
178 | /// use core::convert::Infallible; |
179 | /// use zerocopy::*; |
180 | /// # use zerocopy_derive::*; |
181 | /// |
182 | /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] |
183 | /// #[repr(C, packed)] |
184 | /// struct Bools { |
185 | /// one: bool, |
186 | /// two: bool, |
187 | /// many: [bool], |
188 | /// } |
189 | /// |
190 | /// impl Bools { |
191 | /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { |
192 | /// // Since `Bools: Unaligned`, we can infallibly discard |
193 | /// // the alignment error. |
194 | /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) |
195 | /// } |
196 | /// } |
197 | /// ``` |
198 | #[inline ] |
199 | fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> { |
200 | match err { |
201 | ConvertError::Alignment(e) => ConvertError::Alignment(Infallible::from(e)), |
202 | ConvertError::Size(e) => ConvertError::Size(e), |
203 | ConvertError::Validity(e) => ConvertError::Validity(e), |
204 | } |
205 | } |
206 | } |
207 | |
208 | impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> { |
209 | #[inline ] |
210 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
211 | match self { |
212 | Self::Alignment(e: &A) => f.debug_tuple(name:"Alignment" ).field(e).finish(), |
213 | Self::Size(e: &S) => f.debug_tuple(name:"Size" ).field(e).finish(), |
214 | Self::Validity(e: &V) => f.debug_tuple(name:"Validity" ).field(e).finish(), |
215 | } |
216 | } |
217 | } |
218 | |
219 | /// Produces a human-readable error message. |
220 | /// |
221 | /// The message differs between debug and release builds. When |
222 | /// `debug_assertions` are enabled, this message is verbose and includes |
223 | /// potentially sensitive information. |
224 | impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> { |
225 | #[inline ] |
226 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
227 | match self { |
228 | Self::Alignment(e: &A) => e.fmt(f), |
229 | Self::Size(e: &S) => e.fmt(f), |
230 | Self::Validity(e: &V) => e.fmt(f), |
231 | } |
232 | } |
233 | } |
234 | |
235 | #[cfg (any(zerocopy_core_error_1_81_0, feature = "std" , test))] |
236 | #[cfg_attr (doc_cfg, doc(cfg(all(rust = "1.81.0" , feature = "std" ))))] |
237 | impl<A, S, V> Error for ConvertError<A, S, V> |
238 | where |
239 | A: fmt::Display + fmt::Debug, |
240 | S: fmt::Display + fmt::Debug, |
241 | V: fmt::Display + fmt::Debug, |
242 | { |
243 | } |
244 | |
245 | /// The error emitted if the conversion source is improperly aligned. |
246 | #[derive (PartialEq, Eq)] |
247 | pub struct AlignmentError<Src, Dst: ?Sized> { |
248 | /// The source value involved in the conversion. |
249 | src: Src, |
250 | /// The inner destination type inolved in the conversion. |
251 | /// |
252 | /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s |
253 | /// alignment requirement is greater than one. |
254 | dst: SendSyncPhantomData<Dst>, |
255 | } |
256 | |
257 | impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> { |
258 | /// # Safety |
259 | /// |
260 | /// The caller must ensure that `Dst`'s alignment requirement is greater |
261 | /// than one. |
262 | pub(crate) unsafe fn new_unchecked(src: Src) -> Self { |
263 | // INVARIANT: The caller guarantees that `Dst`'s alignment requirement |
264 | // is greater than one. |
265 | Self { src, dst: SendSyncPhantomData::default() } |
266 | } |
267 | |
268 | /// Produces the source underlying the failed conversion. |
269 | #[inline ] |
270 | pub fn into_src(self) -> Src { |
271 | self.src |
272 | } |
273 | |
274 | pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> { |
275 | // INVARIANT: `with_src` doesn't change the type of `Dst`, so the |
276 | // invariant that `Dst`'s alignment requirement is greater than one is |
277 | // preserved. |
278 | AlignmentError { src: new_src, dst: SendSyncPhantomData::default() } |
279 | } |
280 | |
281 | /// Maps the source value associated with the conversion error. |
282 | /// |
283 | /// This can help mitigate [issues with `Send`, `Sync` and `'static` |
284 | /// bounds][self#send-sync-and-static]. |
285 | /// |
286 | /// # Examples |
287 | /// |
288 | /// ``` |
289 | /// use zerocopy::*; |
290 | /// |
291 | /// let unaligned = Unalign::new(0u16); |
292 | /// |
293 | /// // Attempt to deref `unaligned`. This might fail with an alignment error. |
294 | /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref(); |
295 | /// |
296 | /// // Map the error's source to its address as a usize. |
297 | /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| { |
298 | /// err.map_src(|src| src as *const _ as usize) |
299 | /// }); |
300 | /// ``` |
301 | #[inline ] |
302 | pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> { |
303 | AlignmentError { src: f(self.src), dst: SendSyncPhantomData::default() } |
304 | } |
305 | |
306 | pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> { |
307 | ConvertError::Alignment(self) |
308 | } |
309 | |
310 | /// Format extra details for a verbose, human-readable error message. |
311 | /// |
312 | /// This formatting may include potentially sensitive information. |
313 | fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result |
314 | where |
315 | Src: Deref, |
316 | Dst: KnownLayout, |
317 | { |
318 | #[allow (clippy::as_conversions)] |
319 | let addr = self.src.deref() as *const _ as *const (); |
320 | let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros()); |
321 | |
322 | f.write_str(" \n\nSource type: " )?; |
323 | f.write_str(core::any::type_name::<Src>())?; |
324 | |
325 | f.write_str(" \nSource address: " )?; |
326 | addr.fmt(f)?; |
327 | f.write_str(" (a multiple of " )?; |
328 | addr_align.fmt(f)?; |
329 | f.write_str(")" )?; |
330 | |
331 | f.write_str(" \nDestination type: " )?; |
332 | f.write_str(core::any::type_name::<Dst>())?; |
333 | |
334 | f.write_str(" \nDestination alignment: " )?; |
335 | <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?; |
336 | |
337 | Ok(()) |
338 | } |
339 | } |
340 | |
341 | impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible { |
342 | #[inline (always)] |
343 | fn from(_: AlignmentError<Src, Dst>) -> Infallible { |
344 | // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s |
345 | // alignment requirement is greater than one. In this block, `Dst: |
346 | // Unaligned`, which means that its alignment requirement is equal to |
347 | // one. Thus, it's not possible to reach here at runtime. |
348 | unsafe { core::hint::unreachable_unchecked() } |
349 | } |
350 | } |
351 | |
352 | #[cfg (test)] |
353 | impl<Src, Dst> AlignmentError<Src, Dst> { |
354 | // A convenience constructor so that test code doesn't need to write |
355 | // `unsafe`. |
356 | fn new_checked(src: Src) -> AlignmentError<Src, Dst> { |
357 | assert_ne!(core::mem::align_of::<Dst>(), 1); |
358 | // SAFETY: The preceding assertion guarantees that `Dst`'s alignment |
359 | // requirement is greater than one. |
360 | unsafe { AlignmentError::new_unchecked(src) } |
361 | } |
362 | } |
363 | |
364 | impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> { |
365 | #[inline ] |
366 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
367 | f.debug_struct(name:"AlignmentError" ).finish() |
368 | } |
369 | } |
370 | |
371 | /// Produces a human-readable error message. |
372 | /// |
373 | /// The message differs between debug and release builds. When |
374 | /// `debug_assertions` are enabled, this message is verbose and includes |
375 | /// potentially sensitive information. |
376 | impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst> |
377 | where |
378 | Src: Deref, |
379 | Dst: KnownLayout, |
380 | { |
381 | #[inline ] |
382 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
383 | f.write_str(data:"The conversion failed because the address of the source is not a multiple of the alignment of the destination type." )?; |
384 | |
385 | if cfg!(debug_assertions) { |
386 | self.display_verbose_extras(f) |
387 | } else { |
388 | Ok(()) |
389 | } |
390 | } |
391 | } |
392 | |
393 | #[cfg (any(zerocopy_core_error_1_81_0, feature = "std" , test))] |
394 | #[cfg_attr (doc_cfg, doc(cfg(all(rust = "1.81.0" , feature = "std" ))))] |
395 | impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst> |
396 | where |
397 | Src: Deref, |
398 | Dst: KnownLayout, |
399 | { |
400 | } |
401 | |
402 | impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>> |
403 | for ConvertError<AlignmentError<Src, Dst>, S, V> |
404 | { |
405 | #[inline (always)] |
406 | fn from(err: AlignmentError<Src, Dst>) -> Self { |
407 | Self::Alignment(err) |
408 | } |
409 | } |
410 | |
411 | /// The error emitted if the conversion source is of incorrect size. |
412 | #[derive (PartialEq, Eq)] |
413 | pub struct SizeError<Src, Dst: ?Sized> { |
414 | /// The source value involved in the conversion. |
415 | src: Src, |
416 | /// The inner destination type inolved in the conversion. |
417 | dst: SendSyncPhantomData<Dst>, |
418 | } |
419 | |
420 | impl<Src, Dst: ?Sized> SizeError<Src, Dst> { |
421 | pub(crate) fn new(src: Src) -> Self { |
422 | Self { src, dst: SendSyncPhantomData::default() } |
423 | } |
424 | |
425 | /// Produces the source underlying the failed conversion. |
426 | #[inline ] |
427 | pub fn into_src(self) -> Src { |
428 | self.src |
429 | } |
430 | |
431 | /// Sets the source value associated with the conversion error. |
432 | pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> { |
433 | SizeError { src: new_src, dst: SendSyncPhantomData::default() } |
434 | } |
435 | |
436 | /// Maps the source value associated with the conversion error. |
437 | /// |
438 | /// This can help mitigate [issues with `Send`, `Sync` and `'static` |
439 | /// bounds][self#send-sync-and-static]. |
440 | /// |
441 | /// # Examples |
442 | /// |
443 | /// ``` |
444 | /// use zerocopy::*; |
445 | /// |
446 | /// let source: [u8; 3] = [0, 1, 2]; |
447 | /// |
448 | /// // Try to read a `u32` from `source`. This will fail because there are insufficient |
449 | /// // bytes in `source`. |
450 | /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]); |
451 | /// |
452 | /// // Map the error's source to its size. |
453 | /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| { |
454 | /// err.map_src(|src| src.len()) |
455 | /// }); |
456 | /// ``` |
457 | #[inline ] |
458 | pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> { |
459 | SizeError { src: f(self.src), dst: SendSyncPhantomData::default() } |
460 | } |
461 | |
462 | /// Sets the destination type associated with the conversion error. |
463 | pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> { |
464 | SizeError { src: self.src, dst: SendSyncPhantomData::default() } |
465 | } |
466 | |
467 | /// Converts the error into a general [`ConvertError`]. |
468 | pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> { |
469 | ConvertError::Size(self) |
470 | } |
471 | |
472 | /// Format extra details for a verbose, human-readable error message. |
473 | /// |
474 | /// This formatting may include potentially sensitive information. |
475 | fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result |
476 | where |
477 | Src: Deref, |
478 | Dst: KnownLayout, |
479 | { |
480 | // include the source type |
481 | f.write_str(" \nSource type: " )?; |
482 | f.write_str(core::any::type_name::<Src>())?; |
483 | |
484 | // include the source.deref() size |
485 | let src_size = core::mem::size_of_val(&*self.src); |
486 | f.write_str(" \nSource size: " )?; |
487 | src_size.fmt(f)?; |
488 | f.write_str(" byte" )?; |
489 | if src_size != 1 { |
490 | f.write_char('s' )?; |
491 | } |
492 | |
493 | // if `Dst` is `Sized`, include the `Dst` size |
494 | if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info { |
495 | f.write_str(" \nDestination size: " )?; |
496 | size.fmt(f)?; |
497 | f.write_str(" byte" )?; |
498 | if size != 1 { |
499 | f.write_char('s' )?; |
500 | } |
501 | } |
502 | |
503 | // include the destination type |
504 | f.write_str(" \nDestination type: " )?; |
505 | f.write_str(core::any::type_name::<Dst>())?; |
506 | |
507 | Ok(()) |
508 | } |
509 | } |
510 | |
511 | impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> { |
512 | #[inline ] |
513 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
514 | f.debug_struct(name:"SizeError" ).finish() |
515 | } |
516 | } |
517 | |
518 | /// Produces a human-readable error message. |
519 | /// |
520 | /// The message differs between debug and release builds. When |
521 | /// `debug_assertions` are enabled, this message is verbose and includes |
522 | /// potentially sensitive information. |
523 | impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst> |
524 | where |
525 | Src: Deref, |
526 | Dst: KnownLayout, |
527 | { |
528 | #[inline ] |
529 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
530 | f.write_str(data:"The conversion failed because the source was incorrectly sized to complete the conversion into the destination type." )?; |
531 | if cfg!(debug_assertions) { |
532 | f.write_str(data:" \n" )?; |
533 | self.display_verbose_extras(f)?; |
534 | } |
535 | Ok(()) |
536 | } |
537 | } |
538 | |
539 | #[cfg (any(zerocopy_core_error_1_81_0, feature = "std" , test))] |
540 | #[cfg_attr (doc_cfg, doc(cfg(all(rust = "1.81.0" , feature = "std" ))))] |
541 | impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst> |
542 | where |
543 | Src: Deref, |
544 | Dst: KnownLayout, |
545 | { |
546 | } |
547 | |
548 | impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> { |
549 | #[inline (always)] |
550 | fn from(err: SizeError<Src, Dst>) -> Self { |
551 | Self::Size(err) |
552 | } |
553 | } |
554 | |
555 | /// The error emitted if the conversion source contains invalid data. |
556 | #[derive (PartialEq, Eq)] |
557 | pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> { |
558 | /// The source value involved in the conversion. |
559 | pub(crate) src: Src, |
560 | /// The inner destination type inolved in the conversion. |
561 | dst: SendSyncPhantomData<Dst>, |
562 | } |
563 | |
564 | impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> { |
565 | pub(crate) fn new(src: Src) -> Self { |
566 | Self { src, dst: SendSyncPhantomData::default() } |
567 | } |
568 | |
569 | /// Produces the source underlying the failed conversion. |
570 | #[inline ] |
571 | pub fn into_src(self) -> Src { |
572 | self.src |
573 | } |
574 | |
575 | /// Maps the source value associated with the conversion error. |
576 | /// |
577 | /// This can help mitigate [issues with `Send`, `Sync` and `'static` |
578 | /// bounds][self#send-sync-and-static]. |
579 | /// |
580 | /// # Examples |
581 | /// |
582 | /// ``` |
583 | /// use zerocopy::*; |
584 | /// |
585 | /// let source: u8 = 42; |
586 | /// |
587 | /// // Try to transmute the `source` to a `bool`. This will fail. |
588 | /// let maybe_bool: Result<bool, ValidityError<u8, bool>> = try_transmute!(source); |
589 | /// |
590 | /// // Drop the error's source. |
591 | /// let maybe_bool: Result<bool, ValidityError<(), bool>> = maybe_bool.map_err(|err| { |
592 | /// err.map_src(drop) |
593 | /// }); |
594 | /// ``` |
595 | #[inline ] |
596 | pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> { |
597 | ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() } |
598 | } |
599 | |
600 | /// Converts the error into a general [`ConvertError`]. |
601 | pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> { |
602 | ConvertError::Validity(self) |
603 | } |
604 | |
605 | /// Format extra details for a verbose, human-readable error message. |
606 | /// |
607 | /// This formatting may include potentially sensitive information. |
608 | fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result |
609 | where |
610 | Src: Deref, |
611 | Dst: KnownLayout, |
612 | { |
613 | f.write_str("Destination type: " )?; |
614 | f.write_str(core::any::type_name::<Dst>())?; |
615 | Ok(()) |
616 | } |
617 | } |
618 | |
619 | impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> { |
620 | #[inline ] |
621 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
622 | f.debug_struct(name:"ValidityError" ).finish() |
623 | } |
624 | } |
625 | |
626 | /// Produces a human-readable error message. |
627 | /// |
628 | /// The message differs between debug and release builds. When |
629 | /// `debug_assertions` are enabled, this message is verbose and includes |
630 | /// potentially sensitive information. |
631 | impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst> |
632 | where |
633 | Src: Deref, |
634 | Dst: KnownLayout + TryFromBytes, |
635 | { |
636 | #[inline ] |
637 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
638 | f.write_str(data:"The conversion failed because the source bytes are not a valid value of the destination type." )?; |
639 | if cfg!(debug_assertions) { |
640 | f.write_str(data:" \n\n" )?; |
641 | self.display_verbose_extras(f)?; |
642 | } |
643 | Ok(()) |
644 | } |
645 | } |
646 | |
647 | #[cfg (any(zerocopy_core_error_1_81_0, feature = "std" , test))] |
648 | #[cfg_attr (doc_cfg, doc(cfg(all(rust = "1.81.0" , feature = "std" ))))] |
649 | impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> |
650 | where |
651 | Src: Deref, |
652 | Dst: KnownLayout + TryFromBytes, |
653 | { |
654 | } |
655 | |
656 | impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>> |
657 | for ConvertError<A, S, ValidityError<Src, Dst>> |
658 | { |
659 | #[inline (always)] |
660 | fn from(err: ValidityError<Src, Dst>) -> Self { |
661 | Self::Validity(err) |
662 | } |
663 | } |
664 | |
665 | /// The error type of reference conversions. |
666 | /// |
667 | /// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit |
668 | /// [alignment](AlignmentError) and [size](SizeError) errors. |
669 | // Bounds on generic parameters are not enforced in type aliases, but they do |
670 | // appear in rustdoc. |
671 | #[allow (type_alias_bounds)] |
672 | pub type CastError<Src, Dst: ?Sized> = |
673 | ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>; |
674 | |
675 | impl<Src, Dst: ?Sized> CastError<Src, Dst> { |
676 | /// Produces the source underlying the failed conversion. |
677 | #[inline ] |
678 | pub fn into_src(self) -> Src { |
679 | match self { |
680 | Self::Alignment(e) => e.src, |
681 | Self::Size(e) => e.src, |
682 | Self::Validity(i) => match i {}, |
683 | } |
684 | } |
685 | |
686 | /// Sets the source value associated with the conversion error. |
687 | pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> { |
688 | match self { |
689 | Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)), |
690 | Self::Size(e) => CastError::Size(e.with_src(new_src)), |
691 | Self::Validity(i) => match i {}, |
692 | } |
693 | } |
694 | |
695 | /// Maps the source value associated with the conversion error. |
696 | /// |
697 | /// This can help mitigate [issues with `Send`, `Sync` and `'static` |
698 | /// bounds][self#send-sync-and-static]. |
699 | /// |
700 | /// # Examples |
701 | /// |
702 | /// ``` |
703 | /// use zerocopy::*; |
704 | /// |
705 | /// let source: [u8; 3] = [0, 1, 2]; |
706 | /// |
707 | /// // Try to read a `u32` from `source`. This will fail because there are insufficient |
708 | /// // bytes in `source`. |
709 | /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]); |
710 | /// |
711 | /// // Map the error's source to its size and address. |
712 | /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| { |
713 | /// err.map_src(|src| (src.len(), src.as_ptr() as usize)) |
714 | /// }); |
715 | /// ``` |
716 | #[inline ] |
717 | pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> { |
718 | match self { |
719 | Self::Alignment(e) => CastError::Alignment(e.map_src(f)), |
720 | Self::Size(e) => CastError::Size(e.map_src(f)), |
721 | Self::Validity(i) => match i {}, |
722 | } |
723 | } |
724 | |
725 | /// Converts the error into a general [`ConvertError`]. |
726 | pub(crate) fn into(self) -> TryCastError<Src, Dst> |
727 | where |
728 | Dst: TryFromBytes, |
729 | { |
730 | match self { |
731 | Self::Alignment(e) => TryCastError::Alignment(e), |
732 | Self::Size(e) => TryCastError::Size(e), |
733 | Self::Validity(i) => match i {}, |
734 | } |
735 | } |
736 | } |
737 | |
738 | impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> { |
739 | /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst` |
740 | /// is unaligned. |
741 | /// |
742 | /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment |
743 | /// error, and so the only error that can be encountered at runtime is a |
744 | /// [`SizeError`]. This method permits extracting that `SizeError` |
745 | /// infallibly. |
746 | /// |
747 | /// [`Dst: Unaligned`]: crate::Unaligned |
748 | /// |
749 | /// # Examples |
750 | /// |
751 | /// ```rust |
752 | /// use zerocopy::*; |
753 | /// # use zerocopy_derive::*; |
754 | /// |
755 | /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] |
756 | /// #[repr(C)] |
757 | /// struct UdpHeader { |
758 | /// src_port: [u8; 2], |
759 | /// dst_port: [u8; 2], |
760 | /// length: [u8; 2], |
761 | /// checksum: [u8; 2], |
762 | /// } |
763 | /// |
764 | /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] |
765 | /// #[repr(C, packed)] |
766 | /// struct UdpPacket { |
767 | /// header: UdpHeader, |
768 | /// body: [u8], |
769 | /// } |
770 | /// |
771 | /// impl UdpPacket { |
772 | /// pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> { |
773 | /// // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`. |
774 | /// UdpPacket::ref_from_bytes(bytes).map_err(Into::into) |
775 | /// } |
776 | /// } |
777 | /// ``` |
778 | #[inline (always)] |
779 | fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> { |
780 | match err { |
781 | #[allow (unreachable_code)] |
782 | CastError::Alignment(e) => match Infallible::from(e) {}, |
783 | CastError::Size(e) => e, |
784 | CastError::Validity(i) => match i {}, |
785 | } |
786 | } |
787 | } |
788 | |
789 | /// The error type of fallible reference conversions. |
790 | /// |
791 | /// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`] |
792 | /// may emit [alignment](AlignmentError), [size](SizeError), and |
793 | /// [validity](ValidityError) errors. |
794 | // Bounds on generic parameters are not enforced in type aliases, but they do |
795 | // appear in rustdoc. |
796 | #[allow (type_alias_bounds)] |
797 | pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> = |
798 | ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>; |
799 | |
800 | // TODO(#1139): Remove the `TryFromBytes` here and in other downstream locations |
801 | // (all the way to `ValidityError`) if we determine it's not necessary for rich |
802 | // validity errors. |
803 | impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> { |
804 | /// Produces the source underlying the failed conversion. |
805 | #[inline ] |
806 | pub fn into_src(self) -> Src { |
807 | match self { |
808 | Self::Alignment(e) => e.src, |
809 | Self::Size(e) => e.src, |
810 | Self::Validity(e) => e.src, |
811 | } |
812 | } |
813 | |
814 | /// Maps the source value associated with the conversion error. |
815 | /// |
816 | /// This can help mitigate [issues with `Send`, `Sync` and `'static` |
817 | /// bounds][self#send-sync-and-static]. |
818 | /// |
819 | /// # Examples |
820 | /// |
821 | /// ``` |
822 | /// use core::num::NonZeroU32; |
823 | /// use zerocopy::*; |
824 | /// |
825 | /// let source: [u8; 3] = [0, 0, 0]; |
826 | /// |
827 | /// // Try to read a `NonZeroU32` from `source`. |
828 | /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>> |
829 | /// = NonZeroU32::try_ref_from_bytes(&source[..]); |
830 | /// |
831 | /// // Map the error's source to its size and address. |
832 | /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> = |
833 | /// maybe_u32.map_err(|err| { |
834 | /// err.map_src(|src| (src.len(), src.as_ptr() as usize)) |
835 | /// }); |
836 | /// ``` |
837 | #[inline ] |
838 | pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> { |
839 | match self { |
840 | Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)), |
841 | Self::Size(e) => TryCastError::Size(e.map_src(f)), |
842 | Self::Validity(e) => TryCastError::Validity(e.map_src(f)), |
843 | } |
844 | } |
845 | } |
846 | |
847 | impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> { |
848 | #[inline ] |
849 | fn from(value: CastError<Src, Dst>) -> Self { |
850 | match value { |
851 | CastError::Alignment(e: AlignmentError) => Self::Alignment(e), |
852 | CastError::Size(e: SizeError) => Self::Size(e), |
853 | CastError::Validity(i: Infallible) => match i {}, |
854 | } |
855 | } |
856 | } |
857 | |
858 | /// The error type of fallible read-conversions. |
859 | /// |
860 | /// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may emit |
861 | /// [size](SizeError) and [validity](ValidityError) errors, but not alignment errors. |
862 | // Bounds on generic parameters are not enforced in type aliases, but they do |
863 | // appear in rustdoc. |
864 | #[allow (type_alias_bounds)] |
865 | pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> = |
866 | ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>; |
867 | |
868 | impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> { |
869 | /// Produces the source underlying the failed conversion. |
870 | #[inline ] |
871 | pub fn into_src(self) -> Src { |
872 | match self { |
873 | Self::Alignment(i) => match i {}, |
874 | Self::Size(e) => e.src, |
875 | Self::Validity(e) => e.src, |
876 | } |
877 | } |
878 | |
879 | /// Maps the source value associated with the conversion error. |
880 | /// |
881 | /// This can help mitigate [issues with `Send`, `Sync` and `'static` |
882 | /// bounds][self#send-sync-and-static]. |
883 | /// |
884 | /// # Examples |
885 | /// |
886 | /// ``` |
887 | /// use core::num::NonZeroU32; |
888 | /// use zerocopy::*; |
889 | /// |
890 | /// let source: [u8; 3] = [0, 0, 0]; |
891 | /// |
892 | /// // Try to read a `NonZeroU32` from `source`. |
893 | /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>> |
894 | /// = NonZeroU32::try_read_from_bytes(&source[..]); |
895 | /// |
896 | /// // Map the error's source to its size. |
897 | /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> = |
898 | /// maybe_u32.map_err(|err| { |
899 | /// err.map_src(|src| src.len()) |
900 | /// }); |
901 | /// ``` |
902 | #[inline ] |
903 | pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> { |
904 | match self { |
905 | Self::Alignment(i) => match i {}, |
906 | Self::Size(e) => TryReadError::Size(e.map_src(f)), |
907 | Self::Validity(e) => TryReadError::Validity(e.map_src(f)), |
908 | } |
909 | } |
910 | } |
911 | |
912 | /// The error type of well-aligned, fallible casts. |
913 | /// |
914 | /// This is like [`TryCastError`], but for casts that are always well-aligned. |
915 | /// It is identical to `TryCastError`, except that its alignment error is |
916 | /// [`Infallible`]. |
917 | /// |
918 | /// As of this writing, none of zerocopy's API produces this error directly. |
919 | /// However, it is useful since it permits users to infallibly discard alignment |
920 | /// errors when they can prove statically that alignment errors are impossible. |
921 | /// |
922 | /// # Examples |
923 | /// |
924 | /// ``` |
925 | /// use core::convert::Infallible; |
926 | /// use zerocopy::*; |
927 | /// # use zerocopy_derive::*; |
928 | /// |
929 | /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] |
930 | /// #[repr(C, packed)] |
931 | /// struct Bools { |
932 | /// one: bool, |
933 | /// two: bool, |
934 | /// many: [bool], |
935 | /// } |
936 | /// |
937 | /// impl Bools { |
938 | /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { |
939 | /// // Since `Bools: Unaligned`, we can infallibly discard |
940 | /// // the alignment error. |
941 | /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) |
942 | /// } |
943 | /// } |
944 | /// ``` |
945 | #[allow (type_alias_bounds)] |
946 | pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> = |
947 | ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>; |
948 | |
949 | /// The error type of a failed allocation. |
950 | /// |
951 | /// This type is intended to be deprecated in favor of the standard library's |
952 | /// [`AllocError`] type once it is stabilized. When that happens, this type will |
953 | /// be replaced by a type alias to the standard library type. We do not intend |
954 | /// to treat this as a breaking change; users who wish to avoid breakage should |
955 | /// avoid writing code which assumes that this is *not* such an alias. For |
956 | /// example, implementing the same trait for both types will result in an impl |
957 | /// conflict once this type is an alias. |
958 | /// |
959 | /// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html |
960 | #[derive (Copy, Clone, PartialEq, Eq, Debug)] |
961 | pub struct AllocError; |
962 | |
963 | #[cfg (test)] |
964 | mod tests { |
965 | use super::*; |
966 | |
967 | #[test ] |
968 | fn test_send_sync() { |
969 | // Test that all error types are `Send + Sync` even if `Dst: !Send + |
970 | // !Sync`. |
971 | |
972 | #[allow (dead_code)] |
973 | fn is_send_sync<T: Send + Sync>(_t: T) {} |
974 | |
975 | #[allow (dead_code)] |
976 | fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) { |
977 | is_send_sync(err) |
978 | } |
979 | |
980 | #[allow (dead_code)] |
981 | fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) { |
982 | is_send_sync(err) |
983 | } |
984 | |
985 | #[allow (dead_code)] |
986 | fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>( |
987 | err: ValidityError<Src, Dst>, |
988 | ) { |
989 | is_send_sync(err) |
990 | } |
991 | |
992 | #[allow (dead_code)] |
993 | fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>( |
994 | err: ConvertError< |
995 | AlignmentError<Src, Dst>, |
996 | SizeError<Src, Dst>, |
997 | ValidityError<Src, Dst>, |
998 | >, |
999 | ) { |
1000 | is_send_sync(err) |
1001 | } |
1002 | } |
1003 | |
1004 | #[test ] |
1005 | fn alignment_display() { |
1006 | #[repr (C, align(128))] |
1007 | struct Aligned { |
1008 | bytes: [u8; 128], |
1009 | } |
1010 | |
1011 | impl_known_layout!(elain::Align::<8>); |
1012 | |
1013 | let aligned = Aligned { bytes: [0; 128] }; |
1014 | |
1015 | let bytes = &aligned.bytes[1..]; |
1016 | let addr = crate::util::AsAddress::addr(bytes); |
1017 | assert_eq!( |
1018 | AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), |
1019 | format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type. \n\ |
1020 | \nSource type: &[u8]\ |
1021 | \nSource address: 0x{:x} (a multiple of 1)\ |
1022 | \nDestination type: elain::Align<8>\ |
1023 | \nDestination alignment: 8" , addr) |
1024 | ); |
1025 | |
1026 | let bytes = &aligned.bytes[2..]; |
1027 | let addr = crate::util::AsAddress::addr(bytes); |
1028 | assert_eq!( |
1029 | AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), |
1030 | format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type. \n\ |
1031 | \nSource type: &[u8]\ |
1032 | \nSource address: 0x{:x} (a multiple of 2)\ |
1033 | \nDestination type: elain::Align<8>\ |
1034 | \nDestination alignment: 8" , addr) |
1035 | ); |
1036 | |
1037 | let bytes = &aligned.bytes[3..]; |
1038 | let addr = crate::util::AsAddress::addr(bytes); |
1039 | assert_eq!( |
1040 | AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), |
1041 | format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type. \n\ |
1042 | \nSource type: &[u8]\ |
1043 | \nSource address: 0x{:x} (a multiple of 1)\ |
1044 | \nDestination type: elain::Align<8>\ |
1045 | \nDestination alignment: 8" , addr) |
1046 | ); |
1047 | |
1048 | let bytes = &aligned.bytes[4..]; |
1049 | let addr = crate::util::AsAddress::addr(bytes); |
1050 | assert_eq!( |
1051 | AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), |
1052 | format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type. \n\ |
1053 | \nSource type: &[u8]\ |
1054 | \nSource address: 0x{:x} (a multiple of 4)\ |
1055 | \nDestination type: elain::Align<8>\ |
1056 | \nDestination alignment: 8" , addr) |
1057 | ); |
1058 | } |
1059 | |
1060 | #[test ] |
1061 | fn size_display() { |
1062 | assert_eq!( |
1063 | SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(), |
1064 | "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type. \n\ |
1065 | \nSource type: &[u8]\ |
1066 | \nSource size: 2 bytes\ |
1067 | \nDestination type: [u8]" |
1068 | ); |
1069 | |
1070 | assert_eq!( |
1071 | SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(), |
1072 | "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type. \n\ |
1073 | \nSource type: &[u8]\ |
1074 | \nSource size: 1 byte\ |
1075 | \nDestination size: 2 bytes\ |
1076 | \nDestination type: [u8; 2]" |
1077 | ); |
1078 | } |
1079 | |
1080 | #[test ] |
1081 | fn validity_display() { |
1082 | assert_eq!( |
1083 | ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(), |
1084 | "The conversion failed because the source bytes are not a valid value of the destination type. \n\ |
1085 | \n\ |
1086 | Destination type: bool" |
1087 | ); |
1088 | } |
1089 | } |
1090 | |