about summary refs log tree commit diff
path: root/src/error.rs
blob: 1a0c069a89b06f02342f2d6fc20bfd43b9529037 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use nix;
use std::error;
use std::fmt;
use std::io;
use std::num;

/// This module implements a simple error type to match the errors that can be thrown from the C
/// functions as well as some extra errors resulting from internal validations.
///
/// As this crate exposes an opinionated API to the POSIX queues certain errors have been
/// ignored:
///
/// * ETIMEDOUT: The low-level timed functions are not exported and this error can not occur.
/// * EAGAIN: Non-blocking queue calls are not supported.
/// * EINVAL: Same reason as ETIMEDOUT
/// * EMSGSIZE: The message size is immutable after queue creation and this crate checks it.
/// * ENAMETOOLONG: This crate performs name validation
///
/// If an unexpected error is encountered it will be wrapped appropriately and should be reported
/// as a bug on https://github.com/aprilabank/posix_mq.rs

#[derive(Debug)]
pub enum Error {
    // These errors are raised inside of the library
    InvalidQueueName(&'static str),
    ValueReadingError(io::Error),
    MessageSizeExceeded(),
    MaximumMessageSizeExceeded(),
    MaximumMessageCountExceeded(),

    // These errors match what is described in the man pages (from mq_overview(7) onwards).
    PermissionDenied(),
    InvalidQueueDescriptor(),
    QueueCallInterrupted(),
    QueueAlreadyExists(),
    QueueNotFound(),
    InsufficientMemory(),
    InsufficientSpace(),

    // These two are (hopefully) unlikely in modern systems
    ProcessFileDescriptorLimitReached(),
    SystemFileDescriptorLimitReached(),

    // If an unhandled / unknown / unexpected error occurs this error will be used.
    // In those cases bug reports would be welcome!
    UnknownForeignError(nix::Errno),

    // Some other unexpected / unknown error occured. This is probably an error from
    // the nix crate. Bug reports also welcome for this!
    UnknownInternalError(Option<nix::Error>),
}

impl error::Error for Error {
    fn description(&self) -> &str {
        use Error::*;
        match *self {
            // This error contains more sensible description strings already
            InvalidQueueName(e) => e,
            ValueReadingError(_) => "error reading system configuration for message queues",
            MessageSizeExceeded() => "message is larger than maximum size for specified queue",
            MaximumMessageSizeExceeded() => "specified queue message size exceeds system maximum",
            MaximumMessageCountExceeded() => "specified queue message count exceeds system maximum",
            PermissionDenied() => "permission to the specified queue was denied",
            InvalidQueueDescriptor() => "the internal queue descriptor was invalid",
            QueueCallInterrupted() => "queue method interrupted by signal",
            QueueAlreadyExists() => "the specified queue already exists",
            QueueNotFound() => "the specified queue could not be found",
            InsufficientMemory() => "insufficient memory to call queue method",
            InsufficientSpace() => "insufficient space to call queue method",
            ProcessFileDescriptorLimitReached() =>
                "maximum number of process file descriptors reached",
            SystemFileDescriptorLimitReached() =>
                "maximum number of system file descriptors reached",
            UnknownForeignError(_) => "unknown foreign error occured: please report a bug!",
            UnknownInternalError(_) => "unknown internal error occured: please report a bug!",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Explicitly import this to gain access to Error::description()
        use std::error::Error;
        f.write_str(self.description())
    }
}

/// This from implementation is used to translate errors from the lower-level
/// C-calls into sensible Rust errors.
impl From<nix::Error> for Error {
    fn from(e: nix::Error) -> Self {
        match e {
            nix::Error::Sys(e) => match_errno(e),
            _ => Error::UnknownInternalError(Some(e)),
        }
    }
}

// This implementation is used when reading system queue settings.
impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::ValueReadingError(e)
    }
}

// This implementation is used when parsing system queue settings. The unknown error is returned
// here because the system is probably seriously broken if those files don't contain numbers.
impl From<num::ParseIntError> for Error {
    fn from(_: num::ParseIntError) -> Self {
        Error::UnknownInternalError(None)
    }
}


fn match_errno(err: nix::Errno) -> Error {
    use nix::errno::*;

    match err {
        EACCES => Error::PermissionDenied(),
        EBADF  => Error::InvalidQueueDescriptor(),
        EINTR  => Error::QueueCallInterrupted(),
        EEXIST => Error::QueueAlreadyExists(),
        EMFILE => Error::ProcessFileDescriptorLimitReached(),
        ENFILE => Error::SystemFileDescriptorLimitReached(),
        ENOENT => Error::QueueNotFound(),
        ENOMEM => Error::InsufficientMemory(),
        ENOSPC => Error::InsufficientSpace(),
        _      => Error::UnknownForeignError(err),
    }
}