Skip to main content

zerocopy/
error.rs

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#[cfg(not(no_zerocopy_core_error_1_81_0))]
119use core::error::Error;
120use core::{
121    convert::Infallible,
122    fmt::{self, Debug, Write},
123    ops::Deref,
124};
125#[cfg(all(no_zerocopy_core_error_1_81_0, any(feature = "std", test)))]
126use std::error::Error;
127
128use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};
129#[cfg(doc)]
130use crate::{FromBytes, Ref};
131
132/// Zerocopy's generic error type.
133///
134/// Generally speaking, zerocopy's conversions may fail for one of up to three
135/// reasons:
136/// - [`AlignmentError`]: the conversion source was improperly aligned
137/// - [`SizeError`]: the conversion source was of incorrect size
138/// - [`ValidityError`]: the conversion source contained invalid data
139///
140/// However, not all conversions produce all errors. For instance,
141/// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but
142/// not validity issues. This generic error type captures these
143/// (im)possibilities via parameterization: `A` is parameterized with
144/// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is
145/// parameterized with [`Infallible`].
146///
147/// Zerocopy never uses this type directly in its API. Rather, we provide three
148/// pre-parameterized aliases:
149/// - [`CastError`]: the error type of reference conversions
150/// - [`TryCastError`]: the error type of fallible reference conversions
151/// - [`TryReadError`]: the error type of fallible read conversions
152#[derive(PartialEq, Eq, Clone)]
153pub enum ConvertError<A, S, V> {
154    /// The conversion source was improperly aligned.
155    Alignment(A),
156    /// The conversion source was of incorrect size.
157    Size(S),
158    /// The conversion source contained invalid data.
159    Validity(V),
160}
161
162impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>>
163    for ConvertError<Infallible, S, V>
164{
165    /// Infallibly discards the alignment error from this `ConvertError` since
166    /// `Dst` is unaligned.
167    ///
168    /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
169    /// error. This method permits discarding that alignment error infallibly
170    /// and replacing it with [`Infallible`].
171    ///
172    /// [`Dst: Unaligned`]: crate::Unaligned
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use core::convert::Infallible;
178    /// use zerocopy::*;
179    /// # use zerocopy_derive::*;
180    ///
181    /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
182    /// #[repr(C, packed)]
183    /// struct Bools {
184    ///     one: bool,
185    ///     two: bool,
186    ///     many: [bool],
187    /// }
188    ///
189    /// impl Bools {
190    ///     fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
191    ///         // Since `Bools: Unaligned`, we can infallibly discard
192    ///         // the alignment error.
193    ///         Bools::try_ref_from_bytes(bytes).map_err(Into::into)
194    ///     }
195    /// }
196    /// ```
197    #[inline]
198    fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> {
199        match err {
200            ConvertError::Alignment(e) => {
201                #[allow(unreachable_code)]
202                return ConvertError::Alignment(Infallible::from(e));
203            }
204            ConvertError::Size(e) => ConvertError::Size(e),
205            ConvertError::Validity(e) => ConvertError::Validity(e),
206        }
207    }
208}
209
210impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> {
211    #[inline]
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        match self {
214            Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(),
215            Self::Size(e) => f.debug_tuple("Size").field(e).finish(),
216            Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(),
217        }
218    }
219}
220
221/// Produces a human-readable error message.
222///
223/// The message differs between debug and release builds. When
224/// `debug_assertions` are enabled, this message is verbose and includes
225/// potentially sensitive information.
226impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> {
227    #[inline]
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        match self {
230            Self::Alignment(e) => e.fmt(f),
231            Self::Size(e) => e.fmt(f),
232            Self::Validity(e) => e.fmt(f),
233        }
234    }
235}
236
237#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
238#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
239impl<A, S, V> Error for ConvertError<A, S, V>
240where
241    A: fmt::Display + fmt::Debug,
242    S: fmt::Display + fmt::Debug,
243    V: fmt::Display + fmt::Debug,
244{
245}
246
247/// The error emitted if the conversion source is improperly aligned.
248pub struct AlignmentError<Src, Dst: ?Sized> {
249    /// The source value involved in the conversion.
250    src: Src,
251    /// The inner destination type involved in the conversion.
252    ///
253    /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s
254    /// alignment requirement is greater than one.
255    _dst: SendSyncPhantomData<Dst>,
256}
257
258impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> {
259    /// # Safety
260    ///
261    /// The caller must ensure that `Dst`'s alignment requirement is greater
262    /// than one.
263    pub(crate) unsafe fn new_unchecked(src: Src) -> Self {
264        // INVARIANT: The caller guarantees that `Dst`'s alignment requirement
265        // is greater than one.
266        Self { src, _dst: SendSyncPhantomData::default() }
267    }
268
269    /// Produces the source underlying the failed conversion.
270    #[inline]
271    pub fn into_src(self) -> Src {
272        self.src
273    }
274
275    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> {
276        // INVARIANT: `with_src` doesn't change the type of `Dst`, so the
277        // invariant that `Dst`'s alignment requirement is greater than one is
278        // preserved.
279        AlignmentError { src: new_src, _dst: SendSyncPhantomData::default() }
280    }
281
282    /// Maps the source value associated with the conversion error.
283    ///
284    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
285    /// bounds][self#send-sync-and-static].
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// use zerocopy::*;
291    ///
292    /// let unaligned = Unalign::new(0u16);
293    ///
294    /// // Attempt to deref `unaligned`. This might fail with an alignment error.
295    /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref();
296    ///
297    /// // Map the error's source to its address as a usize.
298    /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| {
299    ///     err.map_src(|src| src as *const _ as usize)
300    /// });
301    /// ```
302    #[inline]
303    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> {
304        AlignmentError { src: f(self.src), _dst: SendSyncPhantomData::default() }
305    }
306
307    pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> {
308        ConvertError::Alignment(self)
309    }
310
311    /// Format extra details for a verbose, human-readable error message.
312    ///
313    /// This formatting may include potentially sensitive information.
314    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
315    where
316        Src: Deref,
317        Dst: KnownLayout,
318    {
319        #[allow(clippy::as_conversions)]
320        let addr = self.src.deref() as *const _ as *const ();
321        let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros());
322
323        f.write_str("\n\nSource type: ")?;
324        f.write_str(core::any::type_name::<Src>())?;
325
326        f.write_str("\nSource address: ")?;
327        addr.fmt(f)?;
328        f.write_str(" (a multiple of ")?;
329        addr_align.fmt(f)?;
330        f.write_str(")")?;
331
332        f.write_str("\nDestination type: ")?;
333        f.write_str(core::any::type_name::<Dst>())?;
334
335        f.write_str("\nDestination alignment: ")?;
336        <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
337
338        Ok(())
339    }
340}
341
342impl<Src: Clone, Dst: ?Sized> Clone for AlignmentError<Src, Dst> {
343    #[inline]
344    fn clone(&self) -> Self {
345        Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() }
346    }
347}
348
349impl<Src: PartialEq, Dst: ?Sized> PartialEq for AlignmentError<Src, Dst> {
350    #[inline]
351    fn eq(&self, other: &Self) -> bool {
352        self.src == other.src
353    }
354}
355
356impl<Src: Eq, Dst: ?Sized> Eq for AlignmentError<Src, Dst> {}
357
358impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible {
359    #[inline(always)]
360    fn from(_: AlignmentError<Src, Dst>) -> Infallible {
361        // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s
362        // alignment requirement is greater than one. In this block, `Dst:
363        // Unaligned`, which means that its alignment requirement is equal to
364        // one. Thus, it's not possible to reach here at runtime.
365        unsafe { core::hint::unreachable_unchecked() }
366    }
367}
368
369#[cfg(test)]
370impl<Src, Dst> AlignmentError<Src, Dst> {
371    // A convenience constructor so that test code doesn't need to write
372    // `unsafe`.
373    fn new_checked(src: Src) -> AlignmentError<Src, Dst> {
374        assert_ne!(core::mem::align_of::<Dst>(), 1);
375        // SAFETY: The preceding assertion guarantees that `Dst`'s alignment
376        // requirement is greater than one.
377        unsafe { AlignmentError::new_unchecked(src) }
378    }
379}
380
381impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
382    #[inline]
383    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384        f.debug_struct("AlignmentError").finish()
385    }
386}
387
388/// Produces a human-readable error message.
389///
390/// The message differs between debug and release builds. When
391/// `debug_assertions` are enabled, this message is verbose and includes
392/// potentially sensitive information.
393impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
394where
395    Src: Deref,
396    Dst: KnownLayout,
397{
398    #[inline]
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?;
401
402        if cfg!(debug_assertions) {
403            self.display_verbose_extras(f)
404        } else {
405            Ok(())
406        }
407    }
408}
409
410#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
411#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
412impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst>
413where
414    Src: Deref,
415    Dst: KnownLayout,
416{
417}
418
419impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
420    for ConvertError<AlignmentError<Src, Dst>, S, V>
421{
422    #[inline(always)]
423    fn from(err: AlignmentError<Src, Dst>) -> Self {
424        Self::Alignment(err)
425    }
426}
427
428/// The error emitted if the conversion source is of incorrect size.
429pub struct SizeError<Src, Dst: ?Sized> {
430    /// The source value involved in the conversion.
431    src: Src,
432    /// The inner destination type involved in the conversion.
433    _dst: SendSyncPhantomData<Dst>,
434}
435
436impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
437    pub(crate) fn new(src: Src) -> Self {
438        Self { src, _dst: SendSyncPhantomData::default() }
439    }
440
441    /// Produces the source underlying the failed conversion.
442    #[inline]
443    pub fn into_src(self) -> Src {
444        self.src
445    }
446
447    /// Sets the source value associated with the conversion error.
448    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> {
449        SizeError { src: new_src, _dst: SendSyncPhantomData::default() }
450    }
451
452    /// Maps the source value associated with the conversion error.
453    ///
454    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
455    /// bounds][self#send-sync-and-static].
456    ///
457    /// # Examples
458    ///
459    /// ```
460    /// use zerocopy::*;
461    ///
462    /// let source: [u8; 3] = [0, 1, 2];
463    ///
464    /// // Try to read a `u32` from `source`. This will fail because there are insufficient
465    /// // bytes in `source`.
466    /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]);
467    ///
468    /// // Map the error's source to its size.
469    /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| {
470    ///     err.map_src(|src| src.len())
471    /// });
472    /// ```
473    #[inline]
474    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> {
475        SizeError { src: f(self.src), _dst: SendSyncPhantomData::default() }
476    }
477
478    /// Sets the destination type associated with the conversion error.
479    pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> {
480        SizeError { src: self.src, _dst: SendSyncPhantomData::default() }
481    }
482
483    /// Converts the error into a general [`ConvertError`].
484    pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> {
485        ConvertError::Size(self)
486    }
487
488    /// Format extra details for a verbose, human-readable error message.
489    ///
490    /// This formatting may include potentially sensitive information.
491    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
492    where
493        Src: Deref,
494        Dst: KnownLayout,
495    {
496        // include the source type
497        f.write_str("\nSource type: ")?;
498        f.write_str(core::any::type_name::<Src>())?;
499
500        // include the source.deref() size
501        let src_size = core::mem::size_of_val(&*self.src);
502        f.write_str("\nSource size: ")?;
503        src_size.fmt(f)?;
504        f.write_str(" byte")?;
505        if src_size != 1 {
506            f.write_char('s')?;
507        }
508
509        // if `Dst` is `Sized`, include the `Dst` size
510        if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info {
511            f.write_str("\nDestination size: ")?;
512            size.fmt(f)?;
513            f.write_str(" byte")?;
514            if size != 1 {
515                f.write_char('s')?;
516            }
517        }
518
519        // include the destination type
520        f.write_str("\nDestination type: ")?;
521        f.write_str(core::any::type_name::<Dst>())?;
522
523        Ok(())
524    }
525}
526
527impl<Src: Clone, Dst: ?Sized> Clone for SizeError<Src, Dst> {
528    #[inline]
529    fn clone(&self) -> Self {
530        Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() }
531    }
532}
533
534impl<Src: PartialEq, Dst: ?Sized> PartialEq for SizeError<Src, Dst> {
535    #[inline]
536    fn eq(&self, other: &Self) -> bool {
537        self.src == other.src
538    }
539}
540
541impl<Src: Eq, Dst: ?Sized> Eq for SizeError<Src, Dst> {}
542
543impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
544    #[inline]
545    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546        f.debug_struct("SizeError").finish()
547    }
548}
549
550/// Produces a human-readable error message.
551///
552/// The message differs between debug and release builds. When
553/// `debug_assertions` are enabled, this message is verbose and includes
554/// potentially sensitive information.
555impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
556where
557    Src: Deref,
558    Dst: KnownLayout,
559{
560    #[inline]
561    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562        f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?;
563        if cfg!(debug_assertions) {
564            f.write_str("\n")?;
565            self.display_verbose_extras(f)?;
566        }
567        Ok(())
568    }
569}
570
571#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
572#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
573impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst>
574where
575    Src: Deref,
576    Dst: KnownLayout,
577{
578}
579
580impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
581    #[inline(always)]
582    fn from(err: SizeError<Src, Dst>) -> Self {
583        Self::Size(err)
584    }
585}
586
587/// The error emitted if the conversion source contains invalid data.
588pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> {
589    /// The source value involved in the conversion.
590    pub(crate) src: Src,
591    /// The inner destination type involved in the conversion.
592    _dst: SendSyncPhantomData<Dst>,
593}
594
595impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
596    pub(crate) fn new(src: Src) -> Self {
597        Self { src, _dst: SendSyncPhantomData::default() }
598    }
599
600    /// Produces the source underlying the failed conversion.
601    #[inline]
602    pub fn into_src(self) -> Src {
603        self.src
604    }
605
606    /// Maps the source value associated with the conversion error.
607    ///
608    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
609    /// bounds][self#send-sync-and-static].
610    ///
611    /// # Examples
612    ///
613    /// ```
614    /// use zerocopy::*;
615    ///
616    /// let source: u8 = 42;
617    ///
618    /// // Try to transmute the `source` to a `bool`. This will fail.
619    /// let maybe_bool: Result<bool, ValidityError<u8, bool>> = try_transmute!(source);
620    ///
621    /// // Drop the error's source.
622    /// let maybe_bool: Result<bool, ValidityError<(), bool>> = maybe_bool.map_err(|err| {
623    ///     err.map_src(drop)
624    /// });
625    /// ```
626    #[inline]
627    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
628        ValidityError { src: f(self.src), _dst: SendSyncPhantomData::default() }
629    }
630
631    /// Converts the error into a general [`ConvertError`].
632    pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> {
633        ConvertError::Validity(self)
634    }
635
636    /// Format extra details for a verbose, human-readable error message.
637    ///
638    /// This formatting may include potentially sensitive information.
639    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
640    where
641        Dst: KnownLayout,
642    {
643        f.write_str("Destination type: ")?;
644        f.write_str(core::any::type_name::<Dst>())?;
645        Ok(())
646    }
647}
648
649impl<Src: Clone, Dst: ?Sized + TryFromBytes> Clone for ValidityError<Src, Dst> {
650    #[inline]
651    fn clone(&self) -> Self {
652        Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() }
653    }
654}
655
656// SAFETY: `ValidityError` contains a single `Self::Inner = Src`, and no other
657// non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` to `f`.
658unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc>
659    for crate::ValidityError<Src, Dst>
660where
661    Dst: TryFromBytes + ?Sized,
662{
663    type Inner = Src;
664    type Mapped = crate::ValidityError<NewSrc, Dst>;
665    #[inline]
666    fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
667        self.map_src(f)
668    }
669}
670
671impl<Src: PartialEq, Dst: ?Sized + TryFromBytes> PartialEq for ValidityError<Src, Dst> {
672    #[inline]
673    fn eq(&self, other: &Self) -> bool {
674        self.src == other.src
675    }
676}
677
678impl<Src: Eq, Dst: ?Sized + TryFromBytes> Eq for ValidityError<Src, Dst> {}
679
680impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
681    #[inline]
682    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683        f.debug_struct("ValidityError").finish()
684    }
685}
686
687/// Produces a human-readable error message.
688///
689/// The message differs between debug and release builds. When
690/// `debug_assertions` are enabled, this message is verbose and includes
691/// potentially sensitive information.
692impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst>
693where
694    Dst: KnownLayout + TryFromBytes,
695{
696    #[inline]
697    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698        f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?;
699        if cfg!(debug_assertions) {
700            f.write_str("\n\n")?;
701            self.display_verbose_extras(f)?;
702        }
703        Ok(())
704    }
705}
706
707#[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]
708#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
709impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> where Dst: KnownLayout + TryFromBytes {}
710
711impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
712    for ConvertError<A, S, ValidityError<Src, Dst>>
713{
714    #[inline(always)]
715    fn from(err: ValidityError<Src, Dst>) -> Self {
716        Self::Validity(err)
717    }
718}
719
720/// The error type of reference conversions.
721///
722/// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit
723/// [alignment](AlignmentError) and [size](SizeError) errors.
724// Bounds on generic parameters are not enforced in type aliases, but they do
725// appear in rustdoc.
726#[allow(type_alias_bounds)]
727pub type CastError<Src, Dst: ?Sized> =
728    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>;
729
730impl<Src, Dst: ?Sized> CastError<Src, Dst> {
731    /// Produces the source underlying the failed conversion.
732    #[inline]
733    pub fn into_src(self) -> Src {
734        match self {
735            Self::Alignment(e) => e.src,
736            Self::Size(e) => e.src,
737            Self::Validity(i) => match i {},
738        }
739    }
740
741    /// Sets the source value associated with the conversion error.
742    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> {
743        match self {
744            Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)),
745            Self::Size(e) => CastError::Size(e.with_src(new_src)),
746            Self::Validity(i) => match i {},
747        }
748    }
749
750    /// Maps the source value associated with the conversion error.
751    ///
752    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
753    /// bounds][self#send-sync-and-static].
754    ///
755    /// # Examples
756    ///
757    /// ```
758    /// use zerocopy::*;
759    ///
760    /// let source: [u8; 3] = [0, 1, 2];
761    ///
762    /// // Try to read a `u32` from `source`. This will fail because there are insufficient
763    /// // bytes in `source`.
764    /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]);
765    ///
766    /// // Map the error's source to its size and address.
767    /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| {
768    ///     err.map_src(|src| (src.len(), src.as_ptr() as usize))
769    /// });
770    /// ```
771    #[inline]
772    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> {
773        match self {
774            Self::Alignment(e) => CastError::Alignment(e.map_src(f)),
775            Self::Size(e) => CastError::Size(e.map_src(f)),
776            Self::Validity(i) => match i {},
777        }
778    }
779
780    /// Converts the error into a general [`ConvertError`].
781    pub(crate) fn into(self) -> TryCastError<Src, Dst>
782    where
783        Dst: TryFromBytes,
784    {
785        match self {
786            Self::Alignment(e) => TryCastError::Alignment(e),
787            Self::Size(e) => TryCastError::Size(e),
788            Self::Validity(i) => match i {},
789        }
790    }
791}
792
793// SAFETY: `CastError` is either a single `AlignmentError` or a single
794// `SizeError`. In either case, it contains a single `Self::Inner = Src`, and no
795// other non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner`
796// to `f`.
797unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc> for crate::CastError<Src, Dst>
798where
799    Dst: ?Sized,
800{
801    type Inner = Src;
802    type Mapped = crate::CastError<NewSrc, Dst>;
803
804    #[inline]
805    fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
806        self.map_src(f)
807    }
808}
809
810impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> {
811    /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst`
812    /// is unaligned.
813    ///
814    /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
815    /// error, and so the only error that can be encountered at runtime is a
816    /// [`SizeError`]. This method permits extracting that `SizeError`
817    /// infallibly.
818    ///
819    /// [`Dst: Unaligned`]: crate::Unaligned
820    ///
821    /// # Examples
822    ///
823    /// ```rust
824    /// use zerocopy::*;
825    /// # use zerocopy_derive::*;
826    ///
827    /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
828    /// #[repr(C)]
829    /// struct UdpHeader {
830    ///     src_port: [u8; 2],
831    ///     dst_port: [u8; 2],
832    ///     length: [u8; 2],
833    ///     checksum: [u8; 2],
834    /// }
835    ///
836    /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
837    /// #[repr(C, packed)]
838    /// struct UdpPacket {
839    ///     header: UdpHeader,
840    ///     body: [u8],
841    /// }
842    ///
843    /// impl UdpPacket {
844    ///     pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> {
845    ///         // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`.
846    ///         UdpPacket::ref_from_bytes(bytes).map_err(Into::into)
847    ///     }
848    /// }
849    /// ```
850    #[inline(always)]
851    fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> {
852        match err {
853            #[allow(unreachable_code)]
854            CastError::Alignment(e) => match Infallible::from(e) {},
855            CastError::Size(e) => e,
856            CastError::Validity(i) => match i {},
857        }
858    }
859}
860
861/// The error type of fallible reference conversions.
862///
863/// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`]
864/// may emit [alignment](AlignmentError), [size](SizeError), and
865/// [validity](ValidityError) errors.
866// Bounds on generic parameters are not enforced in type aliases, but they do
867// appear in rustdoc.
868#[allow(type_alias_bounds)]
869pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> =
870    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
871
872// FIXME(#1139): Remove the `TryFromBytes` here and in other downstream
873// locations (all the way to `ValidityError`) if we determine it's not necessary
874// for rich validity errors.
875impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> {
876    /// Produces the source underlying the failed conversion.
877    #[inline]
878    pub fn into_src(self) -> Src {
879        match self {
880            Self::Alignment(e) => e.src,
881            Self::Size(e) => e.src,
882            Self::Validity(e) => e.src,
883        }
884    }
885
886    /// Maps the source value associated with the conversion error.
887    ///
888    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
889    /// bounds][self#send-sync-and-static].
890    ///
891    /// # Examples
892    ///
893    /// ```
894    /// use core::num::NonZeroU32;
895    /// use zerocopy::*;
896    ///
897    /// let source: [u8; 3] = [0, 0, 0];
898    ///
899    /// // Try to read a `NonZeroU32` from `source`.
900    /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>>
901    ///     = NonZeroU32::try_ref_from_bytes(&source[..]);
902    ///
903    /// // Map the error's source to its size and address.
904    /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> =
905    ///     maybe_u32.map_err(|err| {
906    ///         err.map_src(|src| (src.len(), src.as_ptr() as usize))
907    ///     });
908    /// ```
909    #[inline]
910    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> {
911        match self {
912            Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)),
913            Self::Size(e) => TryCastError::Size(e.map_src(f)),
914            Self::Validity(e) => TryCastError::Validity(e.map_src(f)),
915        }
916    }
917}
918
919impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> {
920    #[inline]
921    fn from(value: CastError<Src, Dst>) -> Self {
922        match value {
923            CastError::Alignment(e) => Self::Alignment(e),
924            CastError::Size(e) => Self::Size(e),
925            CastError::Validity(i) => match i {},
926        }
927    }
928}
929
930/// The error type of fallible read-conversions.
931///
932/// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may
933/// emit [size](SizeError) and [validity](ValidityError) errors, but not
934/// alignment errors.
935// Bounds on generic parameters are not enforced in type aliases, but they do
936// appear in rustdoc.
937#[allow(type_alias_bounds)]
938pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> =
939    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
940
941impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
942    /// Produces the source underlying the failed conversion.
943    #[inline]
944    pub fn into_src(self) -> Src {
945        match self {
946            Self::Alignment(i) => match i {},
947            Self::Size(e) => e.src,
948            Self::Validity(e) => e.src,
949        }
950    }
951
952    /// Maps the source value associated with the conversion error.
953    ///
954    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
955    /// bounds][self#send-sync-and-static].
956    ///
957    /// # Examples
958    ///
959    /// ```
960    /// use core::num::NonZeroU32;
961    /// use zerocopy::*;
962    ///
963    /// let source: [u8; 3] = [0, 0, 0];
964    ///
965    /// // Try to read a `NonZeroU32` from `source`.
966    /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>>
967    ///     = NonZeroU32::try_read_from_bytes(&source[..]);
968    ///
969    /// // Map the error's source to its size.
970    /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> =
971    ///     maybe_u32.map_err(|err| {
972    ///         err.map_src(|src| src.len())
973    ///     });
974    /// ```
975    #[inline]
976    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> {
977        match self {
978            Self::Alignment(i) => match i {},
979            Self::Size(e) => TryReadError::Size(e.map_src(f)),
980            Self::Validity(e) => TryReadError::Validity(e.map_src(f)),
981        }
982    }
983}
984
985/// The error type of well-aligned, fallible casts.
986///
987/// This is like [`TryCastError`], but for casts that are always well-aligned.
988/// It is identical to `TryCastError`, except that its alignment error is
989/// [`Infallible`].
990///
991/// As of this writing, none of zerocopy's API produces this error directly.
992/// However, it is useful since it permits users to infallibly discard alignment
993/// errors when they can prove statically that alignment errors are impossible.
994///
995/// # Examples
996///
997/// ```
998/// use core::convert::Infallible;
999/// use zerocopy::*;
1000/// # use zerocopy_derive::*;
1001///
1002/// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
1003/// #[repr(C, packed)]
1004/// struct Bools {
1005///     one: bool,
1006///     two: bool,
1007///     many: [bool],
1008/// }
1009///
1010/// impl Bools {
1011///     fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
1012///         // Since `Bools: Unaligned`, we can infallibly discard
1013///         // the alignment error.
1014///         Bools::try_ref_from_bytes(bytes).map_err(Into::into)
1015///     }
1016/// }
1017/// ```
1018#[allow(type_alias_bounds)]
1019pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> =
1020    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
1021
1022/// The error type of a failed allocation.
1023///
1024/// This type is intended to be deprecated in favor of the standard library's
1025/// [`AllocError`] type once it is stabilized. When that happens, this type will
1026/// be replaced by a type alias to the standard library type. We do not intend
1027/// to treat this as a breaking change; users who wish to avoid breakage should
1028/// avoid writing code which assumes that this is *not* such an alias. For
1029/// example, implementing the same trait for both types will result in an impl
1030/// conflict once this type is an alias.
1031///
1032/// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html
1033#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1034pub struct AllocError;
1035
1036#[cfg(test)]
1037mod tests {
1038    use core::convert::Infallible;
1039
1040    use super::*;
1041
1042    #[test]
1043    fn test_send_sync() {
1044        // Test that all error types are `Send + Sync` even if `Dst: !Send +
1045        // !Sync`.
1046
1047        #[allow(dead_code)]
1048        fn is_send_sync<T: Send + Sync>(_t: T) {}
1049
1050        #[allow(dead_code)]
1051        fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) {
1052            is_send_sync(err)
1053        }
1054
1055        #[allow(dead_code)]
1056        fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) {
1057            is_send_sync(err)
1058        }
1059
1060        #[allow(dead_code)]
1061        fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
1062            err: ValidityError<Src, Dst>,
1063        ) {
1064            is_send_sync(err)
1065        }
1066
1067        #[allow(dead_code)]
1068        fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
1069            err: ConvertError<
1070                AlignmentError<Src, Dst>,
1071                SizeError<Src, Dst>,
1072                ValidityError<Src, Dst>,
1073            >,
1074        ) {
1075            is_send_sync(err)
1076        }
1077    }
1078
1079    #[test]
1080    fn test_eq_partial_eq_clone() {
1081        // Test that all error types implement `Eq`, `PartialEq`
1082        // and `Clone` if src does
1083        // even if `Dst: !Eq`, `!PartialEq`, `!Clone`.
1084
1085        #[allow(dead_code)]
1086        fn is_eq_partial_eq_clone<T: Eq + PartialEq + Clone>(_t: T) {}
1087
1088        #[allow(dead_code)]
1089        fn alignment_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst>(
1090            err: AlignmentError<Src, Dst>,
1091        ) {
1092            is_eq_partial_eq_clone(err)
1093        }
1094
1095        #[allow(dead_code)]
1096        fn size_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst>(
1097            err: SizeError<Src, Dst>,
1098        ) {
1099            is_eq_partial_eq_clone(err)
1100        }
1101
1102        #[allow(dead_code)]
1103        fn validity_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst: TryFromBytes>(
1104            err: ValidityError<Src, Dst>,
1105        ) {
1106            is_eq_partial_eq_clone(err)
1107        }
1108
1109        #[allow(dead_code)]
1110        fn convert_error_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst: TryFromBytes>(
1111            err: ConvertError<
1112                AlignmentError<Src, Dst>,
1113                SizeError<Src, Dst>,
1114                ValidityError<Src, Dst>,
1115            >,
1116        ) {
1117            is_eq_partial_eq_clone(err)
1118        }
1119    }
1120
1121    #[test]
1122    fn alignment_display() {
1123        #[repr(C, align(128))]
1124        struct Aligned {
1125            bytes: [u8; 128],
1126        }
1127
1128        impl_known_layout!(elain::Align::<8>);
1129
1130        let aligned = Aligned { bytes: [0; 128] };
1131
1132        let bytes = &aligned.bytes[1..];
1133        let addr = crate::util::AsAddress::addr(bytes);
1134        assert_eq!(
1135            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1136            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1137            \nSource type: &[u8]\
1138            \nSource address: 0x{:x} (a multiple of 1)\
1139            \nDestination type: elain::Align<8>\
1140            \nDestination alignment: 8", addr)
1141        );
1142
1143        let bytes = &aligned.bytes[2..];
1144        let addr = crate::util::AsAddress::addr(bytes);
1145        assert_eq!(
1146            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1147            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1148            \nSource type: &[u8]\
1149            \nSource address: 0x{:x} (a multiple of 2)\
1150            \nDestination type: elain::Align<8>\
1151            \nDestination alignment: 8", addr)
1152        );
1153
1154        let bytes = &aligned.bytes[3..];
1155        let addr = crate::util::AsAddress::addr(bytes);
1156        assert_eq!(
1157            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1158            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1159            \nSource type: &[u8]\
1160            \nSource address: 0x{:x} (a multiple of 1)\
1161            \nDestination type: elain::Align<8>\
1162            \nDestination alignment: 8", addr)
1163        );
1164
1165        let bytes = &aligned.bytes[4..];
1166        let addr = crate::util::AsAddress::addr(bytes);
1167        assert_eq!(
1168            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1169            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1170            \nSource type: &[u8]\
1171            \nSource address: 0x{:x} (a multiple of 4)\
1172            \nDestination type: elain::Align<8>\
1173            \nDestination alignment: 8", addr)
1174        );
1175    }
1176
1177    #[test]
1178    fn size_display() {
1179        assert_eq!(
1180            SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(),
1181            "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
1182            \nSource type: &[u8]\
1183            \nSource size: 2 bytes\
1184            \nDestination type: [u8]"
1185        );
1186
1187        assert_eq!(
1188            SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(),
1189            "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
1190            \nSource type: &[u8]\
1191            \nSource size: 1 byte\
1192            \nDestination size: 2 bytes\
1193            \nDestination type: [u8; 2]"
1194        );
1195    }
1196
1197    #[test]
1198    fn validity_display() {
1199        assert_eq!(
1200            ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(),
1201            "The conversion failed because the source bytes are not a valid value of the destination type.\n\
1202            \n\
1203            Destination type: bool"
1204        );
1205    }
1206
1207    #[test]
1208    fn test_convert_error_debug() {
1209        let err: ConvertError<
1210            AlignmentError<&[u8], u16>,
1211            SizeError<&[u8], u16>,
1212            ValidityError<&[u8], bool>,
1213        > = ConvertError::Alignment(AlignmentError::new_checked(&[0u8]));
1214        assert_eq!(format!("{:?}", err), "Alignment(AlignmentError)");
1215
1216        let err: ConvertError<
1217            AlignmentError<&[u8], u16>,
1218            SizeError<&[u8], u16>,
1219            ValidityError<&[u8], bool>,
1220        > = ConvertError::Size(SizeError::new(&[0u8]));
1221        assert_eq!(format!("{:?}", err), "Size(SizeError)");
1222
1223        let err: ConvertError<
1224            AlignmentError<&[u8], u16>,
1225            SizeError<&[u8], u16>,
1226            ValidityError<&[u8], bool>,
1227        > = ConvertError::Validity(ValidityError::new(&[0u8]));
1228        assert_eq!(format!("{:?}", err), "Validity(ValidityError)");
1229    }
1230
1231    #[test]
1232    fn test_convert_error_from_unaligned() {
1233        // u8 is Unaligned
1234        let err: ConvertError<
1235            AlignmentError<&[u8], u8>,
1236            SizeError<&[u8], u8>,
1237            ValidityError<&[u8], bool>,
1238        > = ConvertError::Size(SizeError::new(&[0u8]));
1239        let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> =
1240            ConvertError::from(err);
1241        match converted {
1242            ConvertError::Size(_) => {}
1243            _ => panic!("Expected Size error"),
1244        }
1245    }
1246
1247    #[test]
1248    fn test_alignment_error_display_debug() {
1249        let err: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[0u8]);
1250        assert!(format!("{:?}", err).contains("AlignmentError"));
1251        assert!(format!("{}", err).contains("address of the source is not a multiple"));
1252    }
1253
1254    #[test]
1255    fn test_size_error_display_debug() {
1256        let err: SizeError<&[u8], u16> = SizeError::new(&[0u8]);
1257        assert!(format!("{:?}", err).contains("SizeError"));
1258        assert!(format!("{}", err).contains("source was incorrectly sized"));
1259    }
1260
1261    #[test]
1262    fn test_validity_error_display_debug() {
1263        let err: ValidityError<&[u8], bool> = ValidityError::new(&[0u8]);
1264        assert!(format!("{:?}", err).contains("ValidityError"));
1265        assert!(format!("{}", err).contains("source bytes are not a valid value"));
1266    }
1267
1268    #[test]
1269    fn test_convert_error_display_debug_more() {
1270        let err: ConvertError<
1271            AlignmentError<&[u8], u16>,
1272            SizeError<&[u8], u16>,
1273            ValidityError<&[u8], bool>,
1274        > = ConvertError::Alignment(AlignmentError::new_checked(&[0u8]));
1275        assert!(format!("{}", err).contains("address of the source is not a multiple"));
1276
1277        let err: ConvertError<
1278            AlignmentError<&[u8], u16>,
1279            SizeError<&[u8], u16>,
1280            ValidityError<&[u8], bool>,
1281        > = ConvertError::Size(SizeError::new(&[0u8]));
1282        assert!(format!("{}", err).contains("source was incorrectly sized"));
1283
1284        let err: ConvertError<
1285            AlignmentError<&[u8], u16>,
1286            SizeError<&[u8], u16>,
1287            ValidityError<&[u8], bool>,
1288        > = ConvertError::Validity(ValidityError::new(&[0u8]));
1289        assert!(format!("{}", err).contains("source bytes are not a valid value"));
1290    }
1291
1292    #[test]
1293    fn test_alignment_error_methods() {
1294        let err: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[0u8]);
1295
1296        // into_src
1297        let src = err.clone().into_src();
1298        assert_eq!(src, &[0u8]);
1299
1300        // into
1301        let converted: ConvertError<
1302            AlignmentError<&[u8], u16>,
1303            SizeError<&[u8], u16>,
1304            ValidityError<&[u8], bool>,
1305        > = err.clone().into();
1306        match converted {
1307            ConvertError::Alignment(_) => {}
1308            _ => panic!("Expected Alignment error"),
1309        }
1310
1311        // clone
1312        let cloned = err.clone();
1313        assert_eq!(err, cloned);
1314
1315        // eq
1316        assert_eq!(err, cloned);
1317        let err2: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[1u8]);
1318        assert_ne!(err, err2);
1319    }
1320
1321    #[test]
1322    fn test_convert_error_from_unaligned_variants() {
1323        // u8 is Unaligned
1324        let err: ConvertError<
1325            AlignmentError<&[u8], u8>,
1326            SizeError<&[u8], u8>,
1327            ValidityError<&[u8], bool>,
1328        > = ConvertError::Validity(ValidityError::new(&[0u8]));
1329        let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> =
1330            ConvertError::from(err);
1331        match converted {
1332            ConvertError::Validity(_) => {}
1333            _ => panic!("Expected Validity error"),
1334        }
1335
1336        let err: ConvertError<
1337            AlignmentError<&[u8], u8>,
1338            SizeError<&[u8], u8>,
1339            ValidityError<&[u8], bool>,
1340        > = ConvertError::Size(SizeError::new(&[0u8]));
1341        let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> =
1342            ConvertError::from(err);
1343        match converted {
1344            ConvertError::Size(_) => {}
1345            _ => panic!("Expected Size error"),
1346        }
1347    }
1348}