aboutsummaryrefslogtreecommitdiffstats
path: root/util/error.lua
diff options
context:
space:
mode:
Diffstat (limited to 'util/error.lua')
-rw-r--r--util/error.lua170
1 files changed, 170 insertions, 0 deletions
diff --git a/util/error.lua b/util/error.lua
new file mode 100644
index 00000000..326c01f8
--- /dev/null
+++ b/util/error.lua
@@ -0,0 +1,170 @@
+local id = require "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 "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.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;
+}