PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lua-aplicado/error.lua

http://github.com/lua-aplicado/lua-aplicado
Lua | 193 lines | 148 code | 31 blank | 14 comment | 14 complexity | 058d90fd482552fffa67b8fab3eab77e MD5 | raw file
  1. --------------------------------------------------------------------------------
  2. -- error.lua: error handling convenience wrapper
  3. -- This file is a part of Lua-Aplicado library
  4. -- Copyright (c) Lua-Aplicado authors (see file `COPYRIGHT` for the license)
  5. --------------------------------------------------------------------------------
  6. local debug_traceback = debug.traceback
  7. local arguments,
  8. optional_arguments,
  9. method_arguments
  10. = import 'lua-nucleo/args.lua'
  11. {
  12. 'arguments',
  13. 'optional_arguments',
  14. 'method_arguments'
  15. }
  16. local is_table
  17. = import 'lua-nucleo/type.lua'
  18. {
  19. 'is_table'
  20. }
  21. local unique_object
  22. = import 'lua-nucleo/misc.lua'
  23. {
  24. 'unique_object'
  25. }
  26. local make_loggers
  27. = import 'lua-aplicado/log.lua'
  28. {
  29. 'make_loggers'
  30. }
  31. --------------------------------------------------------------------------------
  32. local log, dbg, spam, log_error = make_loggers("lua-aplicado/error", "ERR")
  33. --------------------------------------------------------------------------------
  34. local error_tag = unique_object()
  35. local is_error_object = function(err)
  36. return not not (is_table(err) and err[1] == error_tag)
  37. end
  38. local error_handler_for_call = function(msg)
  39. if not is_error_object(msg) then
  40. msg = debug_traceback(msg)
  41. log_error(msg)
  42. else
  43. spam("caught: ", debug_traceback(msg[2])) -- Required for debugging
  44. end
  45. return msg
  46. end
  47. local create_error_object = function(error_id)
  48. return { error_tag, error_id }
  49. end
  50. local get_error_id = function(err)
  51. if is_error_object(err) then
  52. return err[2]
  53. end
  54. return nil
  55. end
  56. local throw = function(error_id)
  57. return error(create_error_object(error_id))
  58. end
  59. local pcall_adaptor_for_call = function(status, ...)
  60. if not status then
  61. local err = (...)
  62. local error_id = get_error_id(err)
  63. if error_id ~= nil then
  64. return nil, error_id
  65. end
  66. return error(err) -- Not our error, rethrow
  67. end
  68. return ... -- NOTE: Shouldn't we return true here?
  69. end
  70. --------------------------------------------------------------------------------
  71. local call = function(fn, ...)
  72. local nargs, args = select("#", ...), { ... }
  73. return pcall_adaptor_for_call(
  74. xpcall(
  75. function()
  76. return fn(unpack(args, 1, nargs))
  77. end,
  78. error_handler_for_call
  79. )
  80. )
  81. end
  82. local fail = function(error_id, msg)
  83. arguments(
  84. "string", error_id,
  85. "string", msg
  86. )
  87. log_error(msg)
  88. throw(error_id)
  89. end
  90. local try = function(error_id, result, err, ...)
  91. arguments(
  92. "string", error_id
  93. )
  94. if result == nil then
  95. fail(error_id, err or "no error message")
  96. end
  97. return result, err, ...
  98. end
  99. local rethrow = function(error_id, err)
  100. if not is_error_object(err) then
  101. return fail(error_id, err)
  102. end
  103. error(err) -- Rethrowing our error, ignoring error_id
  104. end
  105. --------------------------------------------------------------------------------
  106. --- Simple finalizer
  107. local xfinally, xcall
  108. do
  109. local pack_pcall_results = function(ok, ...)
  110. return ok, { ... }
  111. end
  112. xfinally = function(fn, cleanup_fn)
  113. local ok, ret = pack_pcall_results(xpcall(fn, error_handler_for_call))
  114. local cleaned, err = xpcall(cleanup_fn, error_handler_for_call)
  115. if cleaned then
  116. if ok then
  117. return unpack(ret)
  118. else
  119. error(ret[1])
  120. end
  121. else
  122. if not ok then
  123. error(ret[1])
  124. else
  125. error(err)
  126. end
  127. end
  128. end
  129. xcall = function(fn, cleanup_fn)
  130. local ok, ret = pack_pcall_results(xpcall(fn, error_handler_for_call))
  131. local cleaned, cleanup_error = xpcall(cleanup_fn, error_handler_for_call)
  132. if cleaned then
  133. if ok then
  134. -- all ok: both fn() and cleanup_fn()
  135. return true, unpack(ret)
  136. else
  137. return nil, ret[1], true
  138. end
  139. else
  140. if not ok then
  141. return nil, ret[1], nil, cleanup_error
  142. else
  143. -- fn() ok, but finalizer is not
  144. return nil, nil, nil, cleanup_error
  145. end
  146. end
  147. end
  148. end
  149. --------------------------------------------------------------------------------
  150. return
  151. {
  152. call = call;
  153. try = try;
  154. fail = fail;
  155. rethrow = rethrow;
  156. xfinally = xfinally;
  157. xcall = xcall;
  158. -- semi-public, for unit tests
  159. create_error_object = create_error_object;
  160. is_error_object = is_error_object;
  161. error_handler_for_call = error_handler_for_call;
  162. }