/
system_error.cr
101 lines (92 loc) · 3.38 KB
/
system_error.cr
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
# This module can be included in any `Exception` subclass that is
# used to wrap some system error (`Errno` or `WinError`)
#
# When included it provides a `from_errno` method (and `from_winerror` on Windows)
# to create exception instances with a description of the original error. It also
# adds an `os_error` property that contains the original system error.
#
# For example:
# ```
# class MyError < Exception
# include SystemError
# end
#
# MyError.from_errno("Something happened")
# ```
module SystemError
macro included
extend ::SystemError::ClassMethods
end
# The original system error wrapped by this exception
{% if flag?(:windows) %}
getter os_error : Errno | WinError | Nil
{% else %}
getter os_error : Errno?
{% end %}
# :nodoc:
protected def os_error=(@os_error)
end
module ClassMethods
# Builds an instance of the exception from a `Errno`
#
# By default it takes the current `errno` value. The `message` is appended
# with the system message corresponding to the `errno`.
# Additional keyword arguments can be passed and they will be forwarded
# to the exception initializer
def from_errno(message : String? = nil, errno : Errno = Errno.value, **opts)
message = self.build_message(message, **opts)
message =
if message
"#{message}: #{errno.message}"
else
errno.message
end
self.new_from_errno(message, errno, **opts).tap do |e|
e.os_error = errno
end
end
# Prepare the message that goes before the system error description
#
# By default it returns the original message unchanged. But that could be
# customized based on the keyword arguments passed to `from_errno` or `from_winerror`.
protected def build_message(message, **opts)
message
end
# Create an instance of the exception that wraps a system error
#
# This is a factory method and by default it creates an instance
# of the current class. It can be overrided to generate different
# classes based on the `errno` or keyword arguments.
protected def new_from_errno(message : String, errno : Errno, **opts)
self.new(message, **opts)
end
{% if flag?(:win32) %}
# Builds an instance of the exception from a `WinError`
#
# By default it takes the current `WinError` value. The `message` is appended
# with the system message corresponding to the `WinError`.
# Additional keyword arguments can be passed and they will be forwarded
# to the exception initializer
def from_winerror(message : String? = nil, winerror : WinError = WinError.value, **opts)
message = self.build_message(message, **opts)
message =
if message
"#{message}: #{winerror.message}"
else
winerror.message
end
self.new_from_winerror(message, winerror, **opts).tap do |e|
e.os_error = winerror
end
end
# Create an instance of the exception that wraps a system error
#
# This is a factory method and by default it creates an instance
# of the current class. It can be overrided to generate different
# classes based on the `winerror` or keyword arguments.
protected def new_from_winerror(message : String, winerror : WinError, **opts)
new_from_errno(message, winerror.to_errno, **opts)
end
{% end %}
end
end