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
parent 11161:f51ed2652602
child 11207: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 }