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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
local id = require "prosody.util.id";
local util_debug; -- only imported on-demand
-- Library configuration (see configure())
local auto_inject_traceback = false;
local error_mt = { __name = "error" };
function error_mt:__tostring()
return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text or "");
end
local function is_error(e)
return getmetatable(e) == error_mt;
end
local function configure(opt)
if opt.auto_inject_traceback ~= nil then
auto_inject_traceback = opt.auto_inject_traceback;
if auto_inject_traceback then
util_debug = require "prosody.util.debug";
end
end
end
-- Do we want any more well-known fields?
-- Or could we just copy all fields from `e`?
-- Sometimes you want variable details in the `text`, how to handle that?
-- Translations?
-- Should the `type` be restricted to the stanza error types or free-form?
-- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr.
local function new(e, context, registry, source)
if is_error(e) then return e; end
local template = registry and registry[e];
if not template then
if type(e) == "table" then
template = {
code = e.code;
type = e.type;
condition = e.condition;
text = e.text;
extra = e.extra;
};
else
template = {};
end
end
context = context or {};
if auto_inject_traceback then
context.traceback = util_debug.get_traceback_table(nil, 2);
end
local error_instance = setmetatable({
instance_id = id.short();
type = template.type or "cancel";
condition = template.condition or "undefined-condition";
text = template.text;
code = template.code;
extra = template.extra;
context = context;
source = source;
}, error_mt);
return error_instance;
end
-- compact --> normal form
local function expand_registry(namespace, registry)
local mapped = {}
for err,template in pairs(registry) do
local e = {
type = template[1];
condition = template[2];
text = template[3];
};
if namespace and template[4] then
e.extra = { namespace = namespace, condition = template[4] };
end
mapped[err] = e;
end
return mapped;
end
local function init(source, namespace, registry)
if type(namespace) == "table" then
-- registry can be given as second argument if namespace is not used
registry, namespace = namespace, nil;
end
local _, protoerr = next(registry, nil);
if protoerr and type(next(protoerr)) == "number" then
registry = expand_registry(namespace, registry);
end
local function wrap(e, context)
if is_error(e) then
return e;
end
local err = new(registry[e] or {
type = "cancel", condition = "undefined-condition"
}, context, registry, source);
err.context.wrapped_error = e;
return err;
end
return {
source = source;
registry = registry;
new = function (e, context)
return new(e, context, registry, source);
end;
coerce = function (ok, err, ...)
if ok then
return ok, err, ...;
end
return nil, wrap(err);
end;
wrap = wrap;
is_error = is_error;
};
end
local function coerce(ok, err, ...)
if ok or is_error(err) then
return ok, err, ...;
end
local new_err = new({
type = "cancel", condition = "undefined-condition"
}, { wrapped_error = err });
return ok, new_err, ...;
end
local function from_stanza(stanza, context, source)
local error_type, condition, text, extra_tag = stanza:get_error();
local error_tag = stanza:get_child("error");
context = context or {};
context.stanza = stanza;
context.by = error_tag and error_tag.attr.by or stanza.attr.from;
local uri;
if condition == "gone" or condition == "redirect" then
uri = error_tag:get_child_text(condition, "urn:ietf:params:xml:ns:xmpp-stanzas");
end
return new({
type = error_type or "cancel";
condition = condition or "undefined-condition";
text = text;
extra = (extra_tag or uri) and {
uri = uri;
tag = extra_tag;
} or nil;
}, context, nil, source);
end
return {
new = new;
init = init;
coerce = coerce;
is_error = is_error;
is_err = is_error; -- COMPAT w/ older 0.12 trunk
from_stanza = from_stanza;
configure = configure;
}
|