Mercurial > prosody-hg
comparison util/error.lua @ 11200:bf8f2da84007
Merge 0.11->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 05 Nov 2020 22:31:25 +0100 |
| parents | f51ed2652602 |
| children | 4e060ae8520b |
comparison
equal
deleted
inserted
replaced
| 11199:6c7c50a4de32 | 11200:bf8f2da84007 |
|---|---|
| 1 local id = require "util.id"; | |
| 2 | |
| 3 -- Library configuration (see configure()) | |
| 4 local auto_inject_traceback = false; | |
| 5 local display_tracebacks = false; | |
| 6 | |
| 7 | |
| 8 local error_mt = { __name = "error" }; | |
| 9 | |
| 10 function error_mt:__tostring() | |
| 11 if display_tracebacks and self.context.traceback then | |
| 12 return ("error<%s:%s:%s:%s>"):format(self.type, self.condition, self.text or "", self.context.traceback); | |
| 13 end | |
| 14 return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text or ""); | |
| 15 end | |
| 16 | |
| 17 local function is_err(e) | |
| 18 return getmetatable(e) == error_mt; | |
| 19 end | |
| 20 | |
| 21 local function configure(opt) | |
| 22 if opt.display_tracebacks ~= nil then | |
| 23 display_tracebacks = opt.display_tracebacks; | |
| 24 end | |
| 25 if opt.auto_inject_traceback ~= nil then | |
| 26 auto_inject_traceback = opt.auto_inject_traceback; | |
| 27 end | |
| 28 end | |
| 29 | |
| 30 -- Do we want any more well-known fields? | |
| 31 -- Or could we just copy all fields from `e`? | |
| 32 -- Sometimes you want variable details in the `text`, how to handle that? | |
| 33 -- Translations? | |
| 34 -- Should the `type` be restricted to the stanza error types or free-form? | |
| 35 -- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr. | |
| 36 | |
| 37 local function new(e, context, registry, source) | |
| 38 if is_err(e) then return e; end | |
| 39 local template = registry and registry[e]; | |
| 40 if not template then | |
| 41 if type(e) == "table" then | |
| 42 template = { | |
| 43 code = e.code; | |
| 44 type = e.type; | |
| 45 condition = e.condition; | |
| 46 text = e.text; | |
| 47 extra = e.extra; | |
| 48 }; | |
| 49 else | |
| 50 template = {}; | |
| 51 end | |
| 52 end | |
| 53 context = context or {}; | |
| 54 | |
| 55 if auto_inject_traceback then | |
| 56 context.traceback = debug.traceback("error stack", 2); | |
| 57 end | |
| 58 | |
| 59 local error_instance = setmetatable({ | |
| 60 instance_id = id.short(); | |
| 61 | |
| 62 type = template.type or "cancel"; | |
| 63 condition = template.condition or "undefined-condition"; | |
| 64 text = template.text; | |
| 65 code = template.code; | |
| 66 extra = template.extra; | |
| 67 | |
| 68 context = context; | |
| 69 source = source; | |
| 70 }, error_mt); | |
| 71 | |
| 72 return error_instance; | |
| 73 end | |
| 74 | |
| 75 -- compact --> normal form | |
| 76 local function expand_registry(namespace, registry) | |
| 77 local mapped = {} | |
| 78 for err,template in pairs(registry) do | |
| 79 local e = { | |
| 80 type = template[1]; | |
| 81 condition = template[2]; | |
| 82 text = template[3]; | |
| 83 }; | |
| 84 if namespace and template[4] then | |
| 85 e.extra = { namespace = namespace, condition = template[4] }; | |
| 86 end | |
| 87 mapped[err] = e; | |
| 88 end | |
| 89 return mapped; | |
| 90 end | |
| 91 | |
| 92 local function init(source, namespace, registry) | |
| 93 if type(namespace) == "table" then | |
| 94 -- registry can be given as second argument if namespace is either not used | |
| 95 registry, namespace = namespace, nil; | |
| 96 end | |
| 97 local _, protoerr = next(registry, nil); | |
| 98 if protoerr and type(next(protoerr)) == "number" then | |
| 99 registry = expand_registry(namespace, registry); | |
| 100 end | |
| 101 return { | |
| 102 source = source; | |
| 103 registry = registry; | |
| 104 new = function (e, context) | |
| 105 return new(e, context, registry, source); | |
| 106 end; | |
| 107 }; | |
| 108 end | |
| 109 | |
| 110 local function coerce(ok, err, ...) | |
| 111 if ok or is_err(err) then | |
| 112 return ok, err, ...; | |
| 113 end | |
| 114 | |
| 115 local new_err = new({ | |
| 116 type = "cancel", condition = "undefined-condition" | |
| 117 }, { wrapped_error = err }); | |
| 118 | |
| 119 return ok, new_err, ...; | |
| 120 end | |
| 121 | |
| 122 local function from_stanza(stanza, context, source) | |
| 123 local error_type, condition, text, extra_tag = stanza:get_error(); | |
| 124 local error_tag = stanza:get_child("error"); | |
| 125 context = context or {}; | |
| 126 context.stanza = stanza; | |
| 127 context.by = error_tag.attr.by or stanza.attr.from; | |
| 128 | |
| 129 local uri; | |
| 130 if condition == "gone" or condition == "redirect" then | |
| 131 uri = error_tag:get_child_text(condition, "urn:ietf:params:xml:ns:xmpp-stanzas"); | |
| 132 end | |
| 133 | |
| 134 return new({ | |
| 135 type = error_type or "cancel"; | |
| 136 condition = condition or "undefined-condition"; | |
| 137 text = text; | |
| 138 extra = (extra_tag or uri) and { | |
| 139 uri = uri; | |
| 140 tag = extra_tag; | |
| 141 } or nil; | |
| 142 }, context, nil, source); | |
| 143 end | |
| 144 | |
| 145 return { | |
| 146 new = new; | |
| 147 init = init; | |
| 148 coerce = coerce; | |
| 149 is_err = is_err; | |
| 150 from_stanza = from_stanza; | |
| 151 configure = configure; | |
| 152 } |
