neli/
err.rs

1//! This is the module that contains the error types used in `neli`
2//!
3//! There are five main types:
4//! * [`Nlmsgerr`] - an application error
5//!   returned from netlink as a packet.
6//! * [`RouterError`] - errors returned by
7//!   [`NlRouter`][crate::router::synchronous::NlRouter].
8//! * [`SocketError`] - errors returned by
9//!   [`NlSocketHandle`][crate::socket::synchronous::NlSocketHandle].
10//! * [`DeError`] - error while deserializing
11//! * [`SerError`] - error while serializing
12//!
13//! # Design decisions
14//! All errors implement [`std::error::Error`] in an attempt to allow
15//! them to be used in conjunction with [`Result`] for easier error
16//! management even at the protocol error level.
17
18use std::{
19    error::Error,
20    fmt::{self, Debug, Display},
21    io::{self, Cursor, ErrorKind},
22    str::Utf8Error,
23    string::FromUtf8Error,
24    sync::Arc,
25};
26
27use derive_builder::{Builder, UninitializedFieldError};
28use getset::Getters;
29
30use crate::{
31    self as neli, FromBytes, FromBytesWithInput, Header, Size, ToBytes, TypeSize,
32    consts::nl::{NlType, NlmF, NlmsgerrAttr},
33    genl::{AttrTypeBuilderError, GenlmsghdrBuilderError, NlattrBuilderError},
34    nl::{Nlmsghdr, NlmsghdrBuilderError},
35    rtnl::{
36        IfaddrmsgBuilderError, IfinfomsgBuilderError, NdaCacheinfoBuilderError, NdmsgBuilderError,
37        RtattrBuilderError, RtgenmsgBuilderError, RtmsgBuilderError, TcmsgBuilderError,
38    },
39    types::{Buffer, GenlBuffer},
40};
41
42/// A special struct that represents the contents of an ACK
43/// returned at the application level.
44#[derive(Builder, Getters, Clone, Debug, PartialEq, Eq, Size, ToBytes, FromBytes)]
45#[neli(header_bound = "T: TypeSize")]
46#[neli(from_bytes_bound = "T: NlType")]
47#[builder(pattern = "owned")]
48pub struct NlmsghdrAck<T> {
49    /// Length of the netlink message
50    #[getset(get = "pub")]
51    nl_len: u32,
52    /// Type of the netlink message
53    #[getset(get = "pub")]
54    nl_type: T,
55    /// Flags indicating properties of the request or response
56    #[getset(get = "pub")]
57    nl_flags: NlmF,
58    /// Sequence number for netlink protocol
59    #[getset(get = "pub")]
60    nl_seq: u32,
61    /// ID of the netlink destination for requests and source for
62    /// responses.
63    #[getset(get = "pub")]
64    nl_pid: u32,
65}
66
67impl NlmsghdrAck<u16> {
68    /// Create a typed ACK from an ACK that can represent all types.
69    pub fn to_typed<T, P>(self) -> Result<NlmsghdrAck<T>, RouterError<T, P>>
70    where
71        T: NlType,
72    {
73        Ok(NlmsghdrAckBuilder::default()
74            .nl_len(self.nl_len)
75            .nl_type(T::from(self.nl_type))
76            .nl_flags(self.nl_flags)
77            .nl_seq(self.nl_seq)
78            .nl_pid(self.nl_pid)
79            .build()?)
80    }
81}
82
83/// A special struct that represents the contents of an error
84/// returned at the application level.
85#[derive(Builder, Getters, Clone, Debug, PartialEq, Eq, Size, ToBytes, FromBytes, Header)]
86#[neli(header_bound = "T: TypeSize")]
87#[neli(from_bytes_bound = "T: NlType + TypeSize")]
88#[neli(from_bytes_bound = "P: FromBytesWithInput<Input = usize>")]
89#[builder(build_fn(skip))]
90#[builder(pattern = "owned")]
91pub struct NlmsghdrErr<T, P> {
92    /// Length of the netlink message
93    #[getset(get = "pub")]
94    #[builder(setter(skip))]
95    nl_len: u32,
96    /// Type of the netlink message
97    #[getset(get = "pub")]
98    nl_type: T,
99    /// Flags indicating properties of the request or response
100    #[getset(get = "pub")]
101    nl_flags: NlmF,
102    /// Sequence number for netlink protocol
103    #[getset(get = "pub")]
104    nl_seq: u32,
105    /// ID of the netlink destination for requests and source for
106    /// responses.
107    #[getset(get = "pub")]
108    nl_pid: u32,
109    /// Payload of netlink message
110    #[neli(input = "nl_len as usize - Self::header_size()")]
111    #[getset(get = "pub")]
112    nl_payload: P,
113}
114
115impl<T, P> NlmsghdrErrBuilder<T, P>
116where
117    T: NlType,
118    P: Size + FromBytesWithInput<Input = usize>,
119{
120    /// Build [`NlmsghdrErr`].
121    pub fn build(self) -> Result<NlmsghdrErr<T, P>, NlmsghdrErrBuilderError> {
122        let nl_type = self.nl_type.ok_or_else(|| {
123            NlmsghdrErrBuilderError::from(UninitializedFieldError::new("nl_type"))
124        })?;
125        let nl_flags = self.nl_flags.unwrap_or(NlmF::empty());
126        let nl_seq = self.nl_seq.unwrap_or(0);
127        let nl_pid = self.nl_pid.unwrap_or(0);
128        let nl_payload = self.nl_payload.ok_or_else(|| {
129            NlmsghdrErrBuilderError::from(UninitializedFieldError::new("nl_payload"))
130        })?;
131
132        let mut nl = NlmsghdrErr {
133            nl_len: 0,
134            nl_type,
135            nl_flags,
136            nl_seq,
137            nl_pid,
138            nl_payload,
139        };
140        nl.nl_len = nl.padded_size() as u32;
141        Ok(nl)
142    }
143}
144
145impl NlmsghdrErr<u16, Buffer> {
146    /// Create a typed error from an error that can represent all types.
147    pub fn to_typed<T, P>(self) -> Result<NlmsghdrErr<T, P>, RouterError<T, P>>
148    where
149        T: NlType,
150        P: Size + FromBytesWithInput<Input = usize>,
151    {
152        Ok(NlmsghdrErrBuilder::default()
153            .nl_type(T::from(self.nl_type))
154            .nl_flags(self.nl_flags)
155            .nl_seq(self.nl_seq)
156            .nl_pid(self.nl_pid)
157            .nl_payload(P::from_bytes_with_input(
158                &mut Cursor::new(self.nl_payload),
159                self.nl_len as usize - Self::header_size(),
160            )?)
161            .build()?)
162    }
163}
164
165/// Struct representing netlink packets containing errors
166#[derive(Builder, Getters, Clone, Debug, PartialEq, Eq, Size, FromBytesWithInput, ToBytes)]
167#[neli(from_bytes_bound = "M: Size + FromBytes")]
168#[builder(pattern = "owned")]
169pub struct Nlmsgerr<M> {
170    /// Error code
171    #[builder(default = "0")]
172    #[getset(get = "pub")]
173    error: libc::c_int,
174    /// Packet header for request that failed
175    #[getset(get = "pub")]
176    #[neli(skip_debug)]
177    nlmsg: M,
178    #[neli(input = "input - error.padded_size() - nlmsg.padded_size()")]
179    /// Contains attributes representing the extended ACK
180    #[builder(default = "GenlBuffer::new()")]
181    #[getset(get = "pub")]
182    ext_ack: GenlBuffer<NlmsgerrAttr, Buffer>,
183}
184
185impl<M> Display for Nlmsgerr<M> {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        write!(f, "{}", io::Error::from_raw_os_error(-self.error))
188    }
189}
190
191impl<M> Error for Nlmsgerr<M> where M: Debug {}
192
193impl Nlmsgerr<NlmsghdrErr<u16, Buffer>> {
194    /// Create a typed error from an error that can represent all types.
195    pub fn to_typed<T, P>(self) -> Result<Nlmsgerr<NlmsghdrErr<T, P>>, RouterError<T, P>>
196    where
197        T: NlType,
198        P: Size + FromBytesWithInput<Input = usize>,
199    {
200        Ok(NlmsgerrBuilder::default()
201            .error(self.error)
202            .nlmsg(self.nlmsg.to_typed()?)
203            .build()?)
204    }
205}
206
207impl Nlmsgerr<NlmsghdrAck<u16>> {
208    /// Create a typed ACK from an ACK that can represent all types.
209    pub fn to_typed<T, P>(self) -> Result<Nlmsgerr<NlmsghdrAck<T>>, RouterError<T, P>>
210    where
211        T: NlType,
212    {
213        Ok(NlmsgerrBuilder::default()
214            .error(self.error)
215            .nlmsg(self.nlmsg.to_typed()?)
216            .build()?)
217    }
218}
219
220#[derive(Debug)]
221#[allow(missing_docs)]
222pub enum BuilderError {
223    #[allow(missing_docs)]
224    Nlmsghdr(NlmsghdrBuilderError),
225    #[allow(missing_docs)]
226    Nlmsgerr(NlmsgerrBuilderError),
227    #[allow(missing_docs)]
228    NlmsghdrErr(NlmsghdrErrBuilderError),
229    #[allow(missing_docs)]
230    Genlmsghdr(GenlmsghdrBuilderError),
231    #[allow(missing_docs)]
232    Nlattr(NlattrBuilderError),
233    #[allow(missing_docs)]
234    AttrType(AttrTypeBuilderError),
235    #[allow(missing_docs)]
236    Ifinfomsg(IfinfomsgBuilderError),
237    #[allow(missing_docs)]
238    Ifaddrmsg(IfaddrmsgBuilderError),
239    #[allow(missing_docs)]
240    Rtgenmsg(RtgenmsgBuilderError),
241    #[allow(missing_docs)]
242    Rtmsg(RtmsgBuilderError),
243    #[allow(missing_docs)]
244    Ndmsg(NdmsgBuilderError),
245    #[allow(missing_docs)]
246    NdaCacheinfo(NdaCacheinfoBuilderError),
247    #[allow(missing_docs)]
248    Tcmsg(TcmsgBuilderError),
249    #[allow(missing_docs)]
250    Rtattr(RtattrBuilderError),
251    #[allow(missing_docs)]
252    NlmsghdrAck(NlmsghdrAckBuilderError),
253}
254
255impl Error for BuilderError {}
256
257impl Display for BuilderError {
258    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259        match self {
260            BuilderError::Nlmsghdr(err) => write!(f, "{err}"),
261            BuilderError::Nlmsgerr(err) => write!(f, "{err}"),
262            BuilderError::NlmsghdrErr(err) => write!(f, "{err}"),
263            BuilderError::Genlmsghdr(err) => write!(f, "{err}"),
264            BuilderError::Nlattr(err) => write!(f, "{err}"),
265            BuilderError::AttrType(err) => write!(f, "{err}"),
266            BuilderError::Ifinfomsg(err) => write!(f, "{err}"),
267            BuilderError::Ifaddrmsg(err) => write!(f, "{err}"),
268            BuilderError::Rtgenmsg(err) => write!(f, "{err}"),
269            BuilderError::Rtmsg(err) => write!(f, "{err}"),
270            BuilderError::Ndmsg(err) => write!(f, "{err}"),
271            BuilderError::NdaCacheinfo(err) => write!(f, "{err}"),
272            BuilderError::Tcmsg(err) => write!(f, "{err}"),
273            BuilderError::Rtattr(err) => write!(f, "{err}"),
274            BuilderError::NlmsghdrAck(err) => write!(f, "{err}"),
275        }
276    }
277}
278
279impl From<NlmsghdrBuilderError> for BuilderError {
280    fn from(e: NlmsghdrBuilderError) -> Self {
281        BuilderError::Nlmsghdr(e)
282    }
283}
284
285impl From<NlmsgerrBuilderError> for BuilderError {
286    fn from(e: NlmsgerrBuilderError) -> Self {
287        BuilderError::Nlmsgerr(e)
288    }
289}
290
291impl From<NlmsghdrErrBuilderError> for BuilderError {
292    fn from(e: NlmsghdrErrBuilderError) -> Self {
293        BuilderError::NlmsghdrErr(e)
294    }
295}
296
297impl From<GenlmsghdrBuilderError> for BuilderError {
298    fn from(e: GenlmsghdrBuilderError) -> Self {
299        BuilderError::Genlmsghdr(e)
300    }
301}
302
303impl From<NlattrBuilderError> for BuilderError {
304    fn from(e: NlattrBuilderError) -> Self {
305        BuilderError::Nlattr(e)
306    }
307}
308
309impl From<AttrTypeBuilderError> for BuilderError {
310    fn from(e: AttrTypeBuilderError) -> Self {
311        BuilderError::AttrType(e)
312    }
313}
314
315impl From<IfinfomsgBuilderError> for BuilderError {
316    fn from(e: IfinfomsgBuilderError) -> Self {
317        BuilderError::Ifinfomsg(e)
318    }
319}
320
321impl From<IfaddrmsgBuilderError> for BuilderError {
322    fn from(e: IfaddrmsgBuilderError) -> Self {
323        BuilderError::Ifaddrmsg(e)
324    }
325}
326
327impl From<RtgenmsgBuilderError> for BuilderError {
328    fn from(e: RtgenmsgBuilderError) -> Self {
329        BuilderError::Rtgenmsg(e)
330    }
331}
332
333impl From<RtmsgBuilderError> for BuilderError {
334    fn from(e: RtmsgBuilderError) -> Self {
335        BuilderError::Rtmsg(e)
336    }
337}
338
339impl From<NdmsgBuilderError> for BuilderError {
340    fn from(e: NdmsgBuilderError) -> Self {
341        BuilderError::Ndmsg(e)
342    }
343}
344
345impl From<NdaCacheinfoBuilderError> for BuilderError {
346    fn from(e: NdaCacheinfoBuilderError) -> Self {
347        BuilderError::NdaCacheinfo(e)
348    }
349}
350
351impl From<TcmsgBuilderError> for BuilderError {
352    fn from(e: TcmsgBuilderError) -> Self {
353        BuilderError::Tcmsg(e)
354    }
355}
356
357impl From<RtattrBuilderError> for BuilderError {
358    fn from(e: RtattrBuilderError) -> Self {
359        BuilderError::Rtattr(e)
360    }
361}
362
363impl From<NlmsghdrAckBuilderError> for BuilderError {
364    fn from(e: NlmsghdrAckBuilderError) -> Self {
365        BuilderError::NlmsghdrAck(e)
366    }
367}
368
369/// Sendable, clonable error that can be sent across channels in the router infrastructure
370/// to provide typed errors to all receivers indicating what went wrong.
371#[derive(Clone, Debug)]
372pub enum RouterError<T, P> {
373    /// Arbitrary message
374    Msg(MsgError),
375    /// errno indicating what went wrong in an IO error.
376    Io(ErrorKind),
377    /// Deserialization error.
378    De(DeError),
379    /// Error from socket infrastructure.
380    Socket(SocketError),
381    /// An error packet sent back by netlink.
382    Nlmsgerr(Nlmsgerr<NlmsghdrErr<T, P>>),
383    /// A bad sequence number or PID was received.
384    BadSeqOrPid(Nlmsghdr<T, P>),
385    /// No ack was received when
386    /// [`NlmF::Ack`][crate::consts::nl::NlmF] was specified in the
387    /// request.
388    NoAck,
389    /// An ack was received when
390    /// [`NlmF::Ack`][crate::consts::nl::NlmF] was not specified in the
391    /// request.
392    UnexpectedAck,
393    /// A channel has closed and message processing cannot continue.
394    ClosedChannel,
395}
396
397impl<T, P> RouterError<T, P> {
398    /// Create a new arbitrary error message.
399    pub fn new<D>(d: D) -> Self
400    where
401        D: Display,
402    {
403        RouterError::Msg(MsgError::new(d.to_string()))
404    }
405}
406
407impl RouterError<u16, Buffer> {
408    /// Convert to typed router error from a router error that can represent all types.
409    pub fn to_typed<T, P>(self) -> Result<RouterError<T, P>, RouterError<T, P>>
410    where
411        T: NlType,
412        P: Size + FromBytesWithInput<Input = usize>,
413    {
414        match self {
415            RouterError::Msg(msg) => Ok(RouterError::Msg(msg)),
416            RouterError::Io(kind) => Ok(RouterError::Io(kind)),
417            RouterError::De(err) => Ok(RouterError::De(err)),
418            RouterError::Socket(err) => Ok(RouterError::Socket(err)),
419            RouterError::Nlmsgerr(err) => Ok(RouterError::Nlmsgerr(err.to_typed()?)),
420            RouterError::BadSeqOrPid(msg) => Ok(RouterError::BadSeqOrPid(msg.to_typed()?)),
421            RouterError::NoAck => Ok(RouterError::NoAck),
422            RouterError::UnexpectedAck => Ok(RouterError::UnexpectedAck),
423            RouterError::ClosedChannel => Ok(RouterError::ClosedChannel),
424        }
425    }
426}
427
428impl<T, P> Display for RouterError<T, P>
429where
430    T: Debug,
431    P: Debug,
432{
433    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
434        match self {
435            RouterError::Msg(msg) => write!(f, "{msg}"),
436            RouterError::Io(kind) => write!(f, "IO error: {kind}"),
437            RouterError::De(err) => write!(f, "Deserialization failed: {err}"),
438            RouterError::Socket(err) => write!(f, "Socket error: {err}"),
439            RouterError::Nlmsgerr(msg) => {
440                write!(f, "Application error was returned by netlink: {msg:?}")
441            }
442            RouterError::BadSeqOrPid(msg) => {
443                write!(f, "A bad sequence number or PID was received: {msg:?}")
444            }
445            RouterError::NoAck => write!(f, "No ACK received"),
446            RouterError::UnexpectedAck => write!(f, "ACK received when none was expected"),
447            RouterError::ClosedChannel => {
448                write!(f, "A channel required for message processing closed")
449            }
450        }
451    }
452}
453
454impl<E, T, P> From<E> for RouterError<T, P>
455where
456    BuilderError: From<E>,
457{
458    fn from(e: E) -> Self {
459        RouterError::new(BuilderError::from(e).to_string())
460    }
461}
462
463impl<T, P> From<DeError> for RouterError<T, P> {
464    fn from(e: DeError) -> Self {
465        RouterError::De(e)
466    }
467}
468
469impl<T, P> From<SocketError> for RouterError<T, P> {
470    fn from(e: SocketError) -> Self {
471        RouterError::Socket(e)
472    }
473}
474
475impl<T, P> From<MsgError> for RouterError<T, P> {
476    fn from(e: MsgError) -> Self {
477        RouterError::Msg(e)
478    }
479}
480
481impl<T, P> Error for RouterError<T, P>
482where
483    T: Debug,
484    P: Debug,
485{
486}
487
488/// General netlink error
489#[derive(Clone, Debug)]
490pub enum SocketError {
491    /// Variant for [`String`]-based messages.
492    Msg(MsgError),
493    /// A serialization error.
494    Ser(SerError),
495    /// A deserialization error.
496    De(DeError),
497    /// IO error.
498    Io(Arc<io::Error>),
499}
500
501impl From<SerError> for SocketError {
502    fn from(err: SerError) -> Self {
503        SocketError::Ser(err)
504    }
505}
506
507impl From<DeError> for SocketError {
508    fn from(err: DeError) -> Self {
509        SocketError::De(err)
510    }
511}
512
513impl From<io::Error> for SocketError {
514    fn from(err: io::Error) -> Self {
515        SocketError::Io(Arc::new(err))
516    }
517}
518
519impl<E> From<E> for SocketError
520where
521    BuilderError: From<E>,
522{
523    fn from(err: E) -> Self {
524        SocketError::new(BuilderError::from(err).to_string())
525    }
526}
527
528impl From<MsgError> for SocketError {
529    fn from(e: MsgError) -> Self {
530        SocketError::Msg(e)
531    }
532}
533
534impl SocketError {
535    /// Create new error from a data type implementing
536    /// [`Display`]
537    pub fn new<D>(s: D) -> Self
538    where
539        D: Display,
540    {
541        SocketError::Msg(MsgError::new(s))
542    }
543}
544
545impl Display for SocketError {
546    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
547        match *self {
548            SocketError::Msg(ref msg) => write!(f, "{msg}"),
549            SocketError::Ser(ref err) => {
550                write!(f, "Serialization error: {err}")
551            }
552            SocketError::De(ref err) => {
553                write!(f, "Deserialization error: {err}")
554            }
555            SocketError::Io(ref err) => {
556                write!(f, "IO error: {err}")
557            }
558        }
559    }
560}
561
562impl Error for SocketError {}
563
564/// [`String`] or [`str`] UTF error.
565#[derive(Clone, Debug)]
566pub enum Utf8 {
567    #[allow(missing_docs)]
568    Str(Utf8Error),
569    #[allow(missing_docs)]
570    String(FromUtf8Error),
571}
572
573impl Display for Utf8 {
574    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575        match self {
576            Utf8::Str(e) => write!(f, "{e}"),
577            Utf8::String(e) => write!(f, "{e}"),
578        }
579    }
580}
581
582/// Serialization error
583#[derive(Clone, Debug)]
584pub enum SerError {
585    /// Abitrary error message.
586    Msg(MsgError),
587    /// IO error.
588    Io(ErrorKind),
589    /// String UTF conversion error.
590    Utf8(Utf8),
591}
592
593impl SerError {
594    /// Create a new error with the given message as description.
595    pub fn new<D>(msg: D) -> Self
596    where
597        D: Display,
598    {
599        SerError::Msg(MsgError::new(msg))
600    }
601}
602
603impl Display for SerError {
604    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605        match self {
606            SerError::Msg(s) => write!(f, "{s}"),
607            SerError::Io(err) => write!(f, "IO error: {err}"),
608            SerError::Utf8(err) => write!(f, "UTF error: {err}"),
609        }
610    }
611}
612
613impl Error for SerError {}
614
615impl From<io::Error> for SerError {
616    fn from(err: io::Error) -> Self {
617        SerError::Io(err.kind())
618    }
619}
620
621impl From<Utf8Error> for SerError {
622    fn from(err: Utf8Error) -> Self {
623        SerError::Utf8(Utf8::Str(err))
624    }
625}
626
627impl From<FromUtf8Error> for SerError {
628    fn from(err: FromUtf8Error) -> Self {
629        SerError::Utf8(Utf8::String(err))
630    }
631}
632
633impl From<MsgError> for SerError {
634    fn from(e: MsgError) -> Self {
635        SerError::Msg(e)
636    }
637}
638
639/// Deserialization error
640#[derive(Clone, Debug)]
641pub enum DeError {
642    /// Abitrary error message.
643    Msg(MsgError),
644    /// IO error
645    Io(ErrorKind),
646    /// String UTF conversion error.
647    Utf8(Utf8),
648    /// Invalid input parameter for [`FromBytesWithInput`].
649    InvalidInput(usize),
650}
651
652impl DeError {
653    /// Create new error from a type implementing
654    /// [`Display`]
655    pub fn new<D>(s: D) -> Self
656    where
657        D: Display,
658    {
659        DeError::Msg(MsgError::new(s))
660    }
661}
662
663impl Display for DeError {
664    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
665        match self {
666            DeError::Msg(s) => write!(f, "{s}"),
667            DeError::Utf8(err) => write!(f, "UTF8 error: {err}"),
668            DeError::Io(err) => write!(f, "IO error: {err}"),
669            DeError::InvalidInput(input) => write!(f, "Invalid input was provided: {input}"),
670        }
671    }
672}
673
674impl Error for DeError {}
675
676impl From<io::Error> for DeError {
677    fn from(err: io::Error) -> Self {
678        DeError::Io(err.kind())
679    }
680}
681
682impl From<Utf8Error> for DeError {
683    fn from(err: Utf8Error) -> Self {
684        DeError::Utf8(Utf8::Str(err))
685    }
686}
687
688impl From<FromUtf8Error> for DeError {
689    fn from(err: FromUtf8Error) -> Self {
690        DeError::Utf8(Utf8::String(err))
691    }
692}
693
694impl<E> From<E> for DeError
695where
696    BuilderError: From<E>,
697{
698    fn from(err: E) -> Self {
699        DeError::new(BuilderError::from(err).to_string())
700    }
701}
702
703impl From<MsgError> for DeError {
704    fn from(e: MsgError) -> Self {
705        DeError::Msg(e)
706    }
707}
708
709/// Arbitrary error message.
710#[derive(Clone, Debug)]
711pub struct MsgError(String);
712
713impl MsgError {
714    /// Construct a new error message.
715    pub fn new<D>(d: D) -> Self
716    where
717        D: Display,
718    {
719        MsgError(d.to_string())
720    }
721}
722
723impl Display for MsgError {
724    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
725        write!(f, "{}", self.0)
726    }
727}
728
729impl Error for MsgError {}