1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * This module provides a portable way of using operating system error codes.
7  *
8  * Copyright: Eugene Wissner 2017-2020.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/error.d,
13  *                 tanya/os/error.d)
14  */
15 module tanya.os.error;
16 
17 import tanya.meta.trait;
18 
19 // Socket API error.
20 private template SAError(int posix, int wsa = posix)
21 {
22     version (Windows)
23     {
24         enum SAError = 10000 + wsa;
25     }
26     else
27     {
28         alias SAError = posix;
29     }
30 }
31 
32 // Error for Windows and Posix separately.
33 private template NativeError(int posix, int win)
34 {
35     version (Windows)
36     {
37         alias NativeError = win;
38     }
39     else
40     {
41         alias NativeError = posix;
42     }
43 }
44 
45 version (Windows)
46 {
47     private enum eProtocolError = -71;
48 }
49 else version (OpenBSD)
50 {
51     private enum eProtocolError = -71;
52 }
53 else
54 {
55     private enum eProtocolError = 71;
56 }
57 
58 /**
59  * System error code.
60  */
61 struct ErrorCode
62 {
63     /**
64      * Error code numbers.
65      */
66     enum ErrorNo : int
67     {
68         /// The operation completed successfully.
69         success                   = 0,
70 
71         /// Operation not permitted.
72         noPermission              = NativeError!(1, 5),
73 
74         /// Interrupted system call.
75         interrupted               = SAError!4,
76 
77         /// Bad file descriptor.
78         badDescriptor             = SAError!9,
79 
80         /// An operation on a non-blocking socket would block.
81         wouldBlock                = SAError!(11, 35),
82 
83         /// Out of memory.
84         noMemory                  = NativeError!(12, 14),
85 
86         /// Access denied.
87         accessDenied              = SAError!13,
88 
89         /// An invalid pointer address detected.
90         fault                     = SAError!14,
91 
92         /// No such device.
93         noSuchDevice              = NativeError!(19, 20),
94 
95         /// An invalid argument was supplied.
96         invalidArgument           = SAError!22,
97 
98         /// The limit on the number of open file descriptors.
99         tooManyDescriptors        = NativeError!(23, 331),
100 
101         /// The limit on the number of open file descriptors.
102         noDescriptors             = SAError!24,
103 
104         /// Broken pipe.
105         brokenPipe                = NativeError!(32, 109),
106 
107         /// The name was too long.
108         nameTooLong               = SAError!(36, 63),
109 
110         /// A socket operation was attempted on a non-socket.
111         notSocket                 = SAError!(88, 38),
112 
113         /// Protocol error.
114         protocolError             = eProtocolError,
115 
116         /// Message too long.
117         messageTooLong            = SAError!(90, 40),
118 
119         /// Wrong protocol type for socket.
120         wrongProtocolType         = SAError!(91, 41),
121 
122         /// Protocol not available.
123         noProtocolOption          = SAError!(92, 42),
124 
125         /// The protocol is not implemented or has not been configured.
126         protocolNotSupported      = SAError!(93, 43),
127 
128         /// The support for the specified socket type does not exist in this
129         /// address family.
130         socketNotSupported        = SAError!(94, 44),
131 
132         /// The address family is no supported by the protocol family.
133         operationNotSupported     = SAError!(95, 45),
134 
135         /// Address family specified is not supported.
136         addressFamilyNotSupported = SAError!(97, 47),
137 
138         /// Address already in use.
139         addressInUse              = SAError!(98, 48),
140 
141         /// The network is not available.
142         networkDown               = SAError!(100, 50),
143 
144         /// No route to host.
145         networkUnreachable        = SAError!(101, 51),
146 
147         /// Network dropped connection because of reset.
148         networkReset              = SAError!(102, 52),
149 
150         /// The connection has been aborted.
151         connectionAborted         = SAError!(103, 53),
152 
153         /// Connection reset by peer.
154         connectionReset           = SAError!(104, 54),
155 
156         /// No free buffer space is available for a socket operation.
157         noBufferSpace             = SAError!(105, 55),
158 
159         /// Transport endpoint is already connected.
160         alreadyConnected          = SAError!(106, 56),
161 
162         /// Transport endpoint is not connected.
163         notConnected              = SAError!(107, 57),
164 
165         /// Cannot send after transport endpoint shutdown.
166         shutdown                  = SAError!(108, 58),
167 
168         /// The connection attempt timed out, or the connected host has failed
169         /// to respond.
170         timedOut                  = SAError!(110, 60),
171 
172         /// Connection refused.
173         connectionRefused         = SAError!(111, 61),
174 
175         /// Host is down.
176         hostDown                  = SAError!(112, 64),
177 
178         /// No route to host.
179         hostUnreachable           = SAError!(113, 65),
180 
181         /// Operation already in progress.
182         alreadyStarted            = SAError!(114, 37),
183 
184         /// Operation now in progress.
185         inProgress                = SAError!(115, 36),
186 
187         /// Operation cancelled.
188         cancelled                 = SAError!(125, 103),
189     }
190 
191     /**
192      * Error descriptions.
193      */
194     private enum ErrorStr : string
195     {
196         success                   = "The operation completed successfully",
197         noPermission              = "Operation not permitted",
198         interrupted               = "Interrupted system call",
199         badDescriptor             = "Bad file descriptor",
200         wouldBlock                = "An operation on a non-blocking socket would block",
201         noMemory                  = "Out of memory",
202         accessDenied              = "Access denied",
203         fault                     = "An invalid pointer address detected",
204         noSuchDevice              = "No such device",
205         invalidArgument           = "An invalid argument was supplied",
206         tooManyDescriptors        = "The limit on the number of open file descriptors",
207         noDescriptors             = "The limit on the number of open file descriptors",
208         brokenPipe                = "Broken pipe",
209         nameTooLong               = "The name was too long",
210         notSocket                 = "A socket operation was attempted on a non-socket",
211         protocolError             = "Protocol error",
212         messageTooLong            = "Message too long",
213         wrongProtocolType         = "Wrong protocol type for socket",
214         noProtocolOption          = "Protocol not available",
215         protocolNotSupported      = "The protocol is not implemented or has not been configured",
216         socketNotSupported        = "Socket type not supported",
217         operationNotSupported     = "The address family is no supported by the protocol family",
218         addressFamilyNotSupported = "Address family specified is not supported",
219         addressInUse              = "Address already in use",
220         networkDown               = "The network is not available",
221         networkUnreachable        = "No route to host",
222         networkReset              = "Network dropped connection because of reset",
223         connectionAborted         = "The connection has been aborted",
224         connectionReset           = "Connection reset by peer",
225         noBufferSpace             = "No free buffer space is available for a socket operation",
226         alreadyConnected          = "Transport endpoint is already connected",
227         notConnected              = "Transport endpoint is not connected",
228         shutdown                  = "Cannot send after transport endpoint shutdown",
229         timedOut                  = "Operation timed out",
230         connectionRefused         = "Connection refused",
231         hostDown                  = "Host is down",
232         hostUnreachable           = "No route to host",
233         alreadyStarted            = "Operation already in progress",
234         inProgress                = "Operation now in progress",
235         cancelled                 = "Operation cancelled",
236     }
237 
238     /**
239      * Constructor.
240      *
241      * Params:
242      *  value = Numeric error code.
243      */
244     this(const ErrorNo value) @nogc nothrow pure @safe
245     {
246         this.value_ = value;
247     }
248 
249     ///
250     @nogc nothrow pure @safe unittest
251     {
252         ErrorCode ec;
253         assert(ec == ErrorCode.success);
254 
255         ec = ErrorCode.fault;
256         assert(ec == ErrorCode.fault);
257     }
258 
259     /**
260      * Resets this $(D_PSYMBOL ErrorCode) to default
261      * ($(D_PSYMBOL ErrorCode.success)).
262      */
263     void reset() @nogc nothrow pure @safe
264     {
265         this.value_ = ErrorNo.success;
266     }
267 
268     ///
269     @nogc nothrow pure @safe unittest
270     {
271         auto ec = ErrorCode(ErrorCode.fault);
272         assert(ec == ErrorCode.fault);
273 
274         ec.reset();
275         assert(ec == ErrorCode.success);
276     }
277 
278     /**
279      * Returns: Numeric error code.
280      */
281     ErrorNo opCast(T : ErrorNo)() const
282     {
283         return this.value_;
284     }
285 
286     /// ditto
287     ErrorNo opCast(T : int)() const
288     {
289         return this.value_;
290     }
291 
292     ///
293     @nogc nothrow pure @safe unittest
294     {
295         ErrorCode ec = ErrorCode.fault;
296         auto errorNo = cast(ErrorCode.ErrorNo) ec;
297 
298         assert(errorNo == ErrorCode.fault);
299         static assert(is(typeof(cast(int) ec)));
300     }
301 
302     /**
303      * Assigns another error code or error code number.
304      *
305      * Params:
306      *  that = Numeric error code.
307      *
308      * Returns: $(D_KEYWORD this).
309      */
310     ref ErrorCode opAssign(const ErrorNo that) return @nogc nothrow pure @safe
311     {
312         this.value_ = that;
313         return this;
314     }
315 
316     /// ditto
317     ref ErrorCode opAssign(const ErrorCode that) return @nogc nothrow pure @safe
318     {
319         this.value_ = that.value_;
320         return this;
321     }
322 
323     ///
324     @nogc nothrow pure @safe unittest
325     {
326         ErrorCode ec;
327         assert(ec == ErrorCode.success);
328 
329         ec = ErrorCode.fault;
330         assert(ec == ErrorCode.fault);
331     }
332 
333     ///
334     @nogc nothrow pure @safe unittest
335     {
336         auto ec1 = ErrorCode(ErrorCode.fault);
337         ErrorCode ec2;
338         assert(ec2 == ErrorCode.success);
339 
340         ec2 = ec1;
341         assert(ec1 == ec2);
342     }
343 
344     /**
345      * Equality with another error code or error code number.
346      *
347      * Params:
348      *  that = Numeric error code.
349      *
350      * Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal.
351      */
352     bool opEquals(const ErrorNo that) const @nogc nothrow pure @safe
353     {
354         return this.value_ == that;
355     }
356 
357     /// ditto
358     bool opEquals(const ErrorCode that) const @nogc nothrow pure @safe
359     {
360         return this.value_ == that.value_;
361     }
362 
363     ///
364     @nogc nothrow pure @safe unittest
365     {
366         ErrorCode ec1 = ErrorCode.fault;
367         ErrorCode ec2 = ErrorCode.accessDenied;
368 
369         assert(ec1 != ec2);
370         assert(ec1 != ErrorCode.accessDenied);
371         assert(ErrorCode.fault != ec2);
372     }
373 
374     ///
375     @nogc nothrow pure @safe unittest
376     {
377         ErrorCode ec1 = ErrorCode.fault;
378         ErrorCode ec2 = ErrorCode.fault;
379 
380         assert(ec1 == ec2);
381         assert(ec1 == ErrorCode.fault);
382         assert(ErrorCode.fault == ec2);
383     }
384 
385     /**
386      * Returns string describing the error number. If a description for a
387      * specific error number is not available, returns $(D_KEYWORD null).
388      *
389      * Returns: String describing the error number.
390      */
391     string toString() const @nogc nothrow pure @safe
392     {
393         foreach (e; __traits(allMembers, ErrorNo))
394         {
395             if (__traits(getMember, ErrorNo, e) == this.value_)
396             {
397                 return __traits(getMember, ErrorStr, e);
398             }
399         }
400         return null;
401     }
402 
403     ///
404     @nogc nothrow pure @safe unittest
405     {
406         ErrorCode ec = ErrorCode.fault;
407         assert(ec.toString() == "An invalid pointer address detected");
408     }
409 
410     private ErrorNo value_ = ErrorNo.success;
411 
412     alias ErrorNo this;
413 }