summaryrefslogtreecommitdiffstats
path: root/nselib/oops.lua
blob: d070d5e0863c1bc464b79279ac4be1624557ac5c (plain)
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
131
--- Useful error stack objects
--
-- Many NSE library functions return a boolean status and an optional error
-- message. The Oops library consists of several simple functions to accumulate
-- these errors and pass them up the stack, resulting in a useful and verbose
-- error message when debugging.
--
-- @author Daniel Miller
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
-- @class module
-- @name oops

local require = require
local setmetatable = setmetatable
local _ENV = require "strict" {}

local nmap = require "nmap"
local debugging = nmap.debugging
local verbosity = nmap.verbosity

local table = require "table"
local concat = table.concat
local insert = table.insert

local Oops = {
  new = function (self, message)
    local o = {message}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  push = function (self, message)
    insert(self, 1, message)
  end,

  __tostring = function (self)
    local banner = "The script encountered an error"
    local sep = ":\n- "
    if debugging() > 0 then
      -- Print full error trace
      return banner .. sep .. concat(self, sep)
    end
    if verbosity() > 0 then
      -- Show just the top error
      return banner .. ": " .. self[1]
    end
    -- By default, no string output shown.
    return ""
  end,
}

--- Add an error message to a stack of errors
--
-- @param message The error message to add to the stack.
-- @param previous (Optional) Any error reported by other functions that failed.
-- @return An Oops object representing the error stack.
err = function (message, previous)
  local result
  if previous then
    if previous.push then
      result = previous
    else
      result = Oops:new(previous)
    end
    result:push(message)
  elseif message.push then
    result = message
  else
    result = Oops:new(message)
  end
  return result
end
local err = err

--- Report an error or return a good value
--
-- If the status is true, just return the message. If it's false, return the
-- message as an Oops object. This can be easily used as the final return value
-- of a script.
-- @param status The return status of the script.
-- @param message The output of the script, or an error message if status is false.
-- @return The message if status is true, or an error message if it is false.
output = function (status, message)
  if status then
    return message
  else
    return err(message)
  end
end
local output = output

--- Report a status and error or return values
--
-- This is intended to wrap a function that returns a status and either an
-- error or some value. If the status is false, the message is added to the
-- stack of errors. Instead of this code:
--
-- <code>
-- local status, value_or_error, value = somefunction(args)
-- if not status then
--   return status, "somefunction failed for some reason"
-- end
-- </code>
--
-- with this instead:
--
-- <code>
-- local status, value_or_error, value = oops.raise("somefunction failed", somefunction(args))
-- if not status then
--   return status, value_or_error
-- end
-- </code>
--
-- but instead of just the one error, you get a stack of errors from
-- <code>somefunction</code> with your own message at the top.
--
-- @param message The error message to report if status is false.
-- @param status The first return value of the function. Treated as boolean, but returned unmodified.
-- @param previous The second return value of the function, or error.
-- @return The same status that was input.
-- @return The rest of the return values, but on error, the message will be added to the stack.
raise = function (message, status, previous, ...)
  local r = previous
  if not status then
    r = err(message, previous)
  end
  return status, r, ...
end

return _ENV