diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-08-02 14:48:01 +0300 |
---|---|---|
committer | V1K1NGbg <victor@ilchev.com> | 2024-08-05 20:51:31 +0300 |
commit | 2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8 (patch) | |
tree | be74689bb9af277471c2a6703cbce675bee5aefe | |
parent | 7cf9d20e32276eae933740b69199376c864f541d (diff) | |
download | luarocks-2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8.tar.gz luarocks-2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8.tar.bz2 luarocks-2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8.zip |
fun, type_check and api update
-rw-r--r-- | src/luarocks/fun.tl | 6 | ||||
-rw-r--r-- | src/luarocks/type_check.tl | 36 | ||||
-rw-r--r-- | src/luarocks/upload/api.tl | 232 |
3 files changed, 138 insertions, 136 deletions
diff --git a/src/luarocks/fun.tl b/src/luarocks/fun.tl index 9d9551ed..16873dc2 100644 --- a/src/luarocks/fun.tl +++ b/src/luarocks/fun.tl | |||
@@ -3,7 +3,7 @@ | |||
3 | local record fun | 3 | local record fun |
4 | end | 4 | end |
5 | 5 | ||
6 | function fun.concat(xs: {any}, ys: {any}): {any} --? not generic becuse lua suports differnt types in one array | 6 | function fun.concat<K>(xs: {K}, ys: {K}): {K} |
7 | local rs = {} | 7 | local rs = {} |
8 | local n = #xs | 8 | local n = #xs |
9 | for i = 1, n do | 9 | for i = 1, n do |
@@ -16,7 +16,7 @@ function fun.concat(xs: {any}, ys: {any}): {any} --? not generic becuse lua supo | |||
16 | return rs | 16 | return rs |
17 | end | 17 | end |
18 | 18 | ||
19 | function fun.contains(xs: {any}, v: any): boolean --? same logic as above fine? | 19 | function fun.contains<K>(xs: {K}, v: K): boolean --? same logic as above fine? |
20 | for _, x in ipairs(xs) do | 20 | for _, x in ipairs(xs) do |
21 | if v == x then | 21 | if v == x then |
22 | return true | 22 | return true |
@@ -55,7 +55,7 @@ function fun.traverse<K, V>(t: {K}, f: function(K): V): {V} | V --? right or {an | |||
55 | end) | 55 | end) |
56 | end | 56 | end |
57 | 57 | ||
58 | function fun.reverse_in(t: {any}): {any} | 58 | function fun.reverse_in<K>(t: {K}): {K} |
59 | for i = 1, math.floor(#t/2) do | 59 | for i = 1, math.floor(#t/2) do |
60 | local m, n = i, #t - i + 1 | 60 | local m, n = i, #t - i + 1 |
61 | local a, b = t[m], t[n] | 61 | local a, b = t[m], t[n] |
diff --git a/src/luarocks/type_check.tl b/src/luarocks/type_check.tl index 09dfa96d..1252133d 100644 --- a/src/luarocks/type_check.tl +++ b/src/luarocks/type_check.tl | |||
@@ -14,7 +14,7 @@ local vers = require("luarocks.core.vers") | |||
14 | type_check.MAGIC_PLATFORMS = 0xEBABEFAC | 14 | type_check.MAGIC_PLATFORMS = 0xEBABEFAC |
15 | 15 | ||
16 | do | 16 | do |
17 | local function fill_in_version(tbl, version?) | 17 | local function fill_in_version(tbl: {any: any}, version?: string) --! tighter bound? --? What is filled if no version is provided |
18 | for _, v in pairs(tbl) do | 18 | for _, v in pairs(tbl) do |
19 | if v is table then | 19 | if v is table then |
20 | if v._version == nil then | 20 | if v._version == nil then |
@@ -25,9 +25,9 @@ do | |||
25 | end | 25 | end |
26 | end | 26 | end |
27 | 27 | ||
28 | local function expand_magic_platforms(tbl) | 28 | local function expand_magic_platforms(tbl: {any: any}) --! tbl type |
29 | for k,v in pairs(tbl) do | 29 | for k,v in pairs(tbl) do |
30 | if v == type_check.MAGIC_PLATFORMS then | 30 | if v == type_check.MAGIC_PLATFORMS then --! v is int, next line it is not |
31 | tbl[k] = { | 31 | tbl[k] = { |
32 | _any = util.deep_copy(tbl) | 32 | _any = util.deep_copy(tbl) |
33 | } | 33 | } |
@@ -43,9 +43,9 @@ do | |||
43 | -- and the value is a schema specification. Schema versions are considered | 43 | -- and the value is a schema specification. Schema versions are considered |
44 | -- incremental: version "2.0" only needs to specify what's new/changed from | 44 | -- incremental: version "2.0" only needs to specify what's new/changed from |
45 | -- version "1.0". | 45 | -- version "1.0". |
46 | function type_check.declare_schemas(inputs: {string: any}): {any: any}, {any} --? | 46 | function type_check.declare_schemas(inputs: {string: {any: any}}): {string : {any : any}}, {string} --! schema type: {string: {any: any}} |
47 | local schemas = {} | 47 | local schemas: {string: {any: any}} = {} |
48 | local parent_version | 48 | local parent_version: string |
49 | 49 | ||
50 | local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions)) | 50 | local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions)) |
51 | 51 | ||
@@ -68,7 +68,7 @@ end | |||
68 | 68 | ||
69 | -------------------------------------------------------------------------------- | 69 | -------------------------------------------------------------------------------- |
70 | 70 | ||
71 | local function check_version(version: string, typetbl, context: string) | 71 | local function check_version(version: string, typetbl: {string: string}, context: string): boolean, string --! typetbl |
72 | local typetbl_version = typetbl._version or "1.0" | 72 | local typetbl_version = typetbl._version or "1.0" |
73 | if vers.compare_versions(typetbl_version, version) then | 73 | if vers.compare_versions(typetbl_version, version) then |
74 | if context == "" then | 74 | if context == "" then |
@@ -80,6 +80,8 @@ local function check_version(version: string, typetbl, context: string) | |||
80 | return true | 80 | return true |
81 | end | 81 | end |
82 | 82 | ||
83 | |||
84 | |||
83 | --- Type check an object. | 85 | --- Type check an object. |
84 | -- The object is compared against an archetypical value | 86 | -- The object is compared against an archetypical value |
85 | -- matching the expected type -- the actual values don't matter, | 87 | -- matching the expected type -- the actual values don't matter, |
@@ -92,8 +94,7 @@ end | |||
92 | -- @return boolean or (nil, string): true if type checking | 94 | -- @return boolean or (nil, string): true if type checking |
93 | -- succeeded, or nil and an error message if it failed. | 95 | -- succeeded, or nil and an error message if it failed. |
94 | -- @see type_check_table | 96 | -- @see type_check_table |
95 | local function type_check_item(version, item, typetbl, context) | 97 | local function type_check_item(version: string, item: any, typetbl: {string: string}, context: string): boolean, string --! typetbl |
96 | assert(type(version) == "string") | ||
97 | 98 | ||
98 | if typetbl._version and typetbl._version ~= "1.0" then | 99 | if typetbl._version and typetbl._version ~= "1.0" then |
99 | local ok, err = check_version(version, typetbl, context) | 100 | local ok, err = check_version(version, typetbl, context) |
@@ -115,9 +116,9 @@ local function type_check_item(version, item, typetbl, context) | |||
115 | end | 116 | end |
116 | local pattern = typetbl._pattern | 117 | local pattern = typetbl._pattern |
117 | if pattern then | 118 | if pattern then |
118 | if not item:match("^"..pattern.."$") then | 119 | if not item:match("^"..pattern.."$") then --! cast or tostring |
119 | local what = typetbl._name or ("'"..pattern.."'") | 120 | local what = typetbl._name or ("'"..pattern.."'") |
120 | return nil, "Type mismatch on field "..context..": invalid value '"..item.."' does not match " .. what | 121 | return nil, "Type mismatch on field "..context..": invalid value '"..item.."' does not match " .. what --! cast or tostring |
121 | end | 122 | end |
122 | end | 123 | end |
123 | elseif expected_type == "table" then | 124 | elseif expected_type == "table" then |
@@ -132,10 +133,10 @@ local function type_check_item(version, item, typetbl, context) | |||
132 | return true | 133 | return true |
133 | end | 134 | end |
134 | 135 | ||
135 | local function mkfield(context, field) | 136 | local function mkfield(context: string, field: any): string |
136 | if context == "" then | 137 | if context == "" then |
137 | return tostring(field) | 138 | return tostring(field) |
138 | elseif type(field) == "string" then | 139 | elseif field is string then |
139 | return context.."."..field | 140 | return context.."."..field |
140 | else | 141 | else |
141 | return context.."["..tostring(field).."]" | 142 | return context.."["..tostring(field).."]" |
@@ -164,10 +165,7 @@ end | |||
164 | -- to be used by error messages. | 165 | -- to be used by error messages. |
165 | -- @return boolean or (nil, string): true if type checking | 166 | -- @return boolean or (nil, string): true if type checking |
166 | -- succeeded, or nil and an error message if it failed. | 167 | -- succeeded, or nil and an error message if it failed. |
167 | function type_check.type_check_table(version, tbl, typetbl, context) | 168 | function type_check.type_check_table(version: string, tbl: {any: any}, typetbl: {string: string}, context: string): boolean, string --! tbl and typetbl types |
168 | assert(type(version) == "string") | ||
169 | assert(type(tbl) == "table") | ||
170 | assert(type(typetbl) == "table") | ||
171 | 169 | ||
172 | local ok, err = check_version(version, typetbl, context) | 170 | local ok, err = check_version(version, typetbl, context) |
173 | if not ok then | 171 | if not ok then |
@@ -188,7 +186,7 @@ function type_check.type_check_table(version, tbl, typetbl, context) | |||
188 | end | 186 | end |
189 | end | 187 | end |
190 | for k, v in pairs(typetbl) do | 188 | for k, v in pairs(typetbl) do |
191 | if k:sub(1,1) ~= "_" and v._mandatory then | 189 | if k:sub(1,1) ~= "_" and v._mandatory then --! |
192 | if not tbl[k] then | 190 | if not tbl[k] then |
193 | return nil, "Mandatory field "..mkfield(context, k).." is missing." | 191 | return nil, "Mandatory field "..mkfield(context, k).." is missing." |
194 | end | 192 | end |
@@ -197,7 +195,7 @@ function type_check.type_check_table(version, tbl, typetbl, context) | |||
197 | return true | 195 | return true |
198 | end | 196 | end |
199 | 197 | ||
200 | function type_check.check_undeclared_globals(globals, typetbl) | 198 | function type_check.check_undeclared_globals(globals: {string: any}, typetbl: {string: string}): boolean, string --! tbl and typetbl types |
201 | local undeclared = {} | 199 | local undeclared = {} |
202 | for glob, _ in pairs(globals) do | 200 | for glob, _ in pairs(globals) do |
203 | if not (typetbl[glob] or typetbl["MUST_"..glob]) then | 201 | if not (typetbl[glob] or typetbl["MUST_"..glob]) then |
diff --git a/src/luarocks/upload/api.tl b/src/luarocks/upload/api.tl index fd837fd8..0c94a6ee 100644 --- a/src/luarocks/upload/api.tl +++ b/src/luarocks/upload/api.tl | |||
@@ -15,9 +15,13 @@ local record Api | |||
15 | raw_method: function(Api, string) | 15 | raw_method: function(Api, string) |
16 | record config | 16 | record config |
17 | server: any --! why to string | 17 | server: any --! why to string |
18 | key: string | ||
18 | end | 19 | end |
20 | debug: boolean | ||
19 | end | 21 | end |
20 | 22 | ||
23 | local type Parameters = multipart.Parameters | ||
24 | |||
21 | local function upload_config_file(): string | 25 | local function upload_config_file(): string |
22 | if not cfg.config_files.user.file then | 26 | if not cfg.config_files.user.file then |
23 | return nil | 27 | return nil |
@@ -32,7 +36,7 @@ function Api:load_config(): {any: any} --? tighter bound? | |||
32 | return config | 36 | return config |
33 | end | 37 | end |
34 | 38 | ||
35 | function Api:save_config() | 39 | function Api:save_config(): nil, string --! nil? |
36 | -- Test configuration before saving it. | 40 | -- Test configuration before saving it. |
37 | local res, err = self:raw_method("status") | 41 | local res, err = self:raw_method("status") |
38 | if not res then | 42 | if not res then |
@@ -90,25 +94,25 @@ function Api:method(...) | |||
90 | return res | 94 | return res |
91 | end | 95 | end |
92 | 96 | ||
93 | function Api.raw_method(self: Api, path: string, ...) --! path, ... type | 97 | function Api:raw_method(path: string, ...) --! path, ... type |
94 | self:check_version() | 98 | self:check_version() |
95 | local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. path | 99 | local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. path |
96 | return self:request(url, ...) | 100 | return self:request(url, ...) |
97 | end | 101 | end |
98 | 102 | ||
99 | local function encode_query_string(t, sep) | 103 | local function encode_query_string(t: Parameters, sep?: string): string |
100 | if sep == nil then | 104 | if sep == nil then |
101 | sep = "&" | 105 | sep = "&" |
102 | end | 106 | end |
103 | local i = 0 | 107 | local i = 0 |
104 | local buf = { } | 108 | local buf: {string} = { } |
105 | for k, v in pairs(t) do | 109 | for k, v in pairs(t.map) do --! pairs problem for Parameters |
106 | if type(k) == "number" and type(v) == "table" then | 110 | if k is number and v is {string} then |
107 | k, v = v[1], v[2] | 111 | k, v = v[1], v[2] |
108 | end | 112 | end |
109 | buf[i + 1] = multipart.url_escape(k) | 113 | buf[i + 1] = multipart.url_escape(k as string) --! cast |
110 | buf[i + 2] = "=" | 114 | buf[i + 2] = "=" |
111 | buf[i + 3] = multipart.url_escape(v) | 115 | buf[i + 3] = multipart.url_escape(v as string) --! cast |
112 | buf[i + 4] = sep | 116 | buf[i + 4] = sep |
113 | i = i + 4 | 117 | i = i + 4 |
114 | end | 118 | end |
@@ -116,140 +120,140 @@ local function encode_query_string(t, sep) | |||
116 | return table.concat(buf) | 120 | return table.concat(buf) |
117 | end | 121 | end |
118 | 122 | ||
119 | local function redact_api_url(url) | 123 | local function redact_api_url(url: any): string |
120 | url = tostring(url) | 124 | url = tostring(url) |
121 | return (url:gsub(".*/api/[^/]+/[^/]+", "")) or "" | 125 | return ((url as string):gsub(".*/api/[^/]+/[^/]+", "")) or "" --! cast |
122 | end | 126 | end |
123 | 127 | ||
124 | local ltn12_ok, ltn12 = pcall(require, "ltn12") | 128 | local ltn12_ok, ltn12 = pcall(require, "ltn12") |
125 | if not ltn12_ok then -- If not using LuaSocket and/or LuaSec... | 129 | if not ltn12_ok then -- If not using LuaSocket and/or LuaSec... |
126 | 130 | ||
127 | function Api.request(self: Api, url: string, params?, post_params?): {string : any}, string | 131 | function Api:request(url: string, params?: Parameters, post_params?: Parameters): {string : any}, string |
128 | local vars = cfg.variables | 132 | local vars = cfg.variables |
129 | |||
130 | if fs.which_tool("downloader") == "wget" then | ||
131 | local curl_ok, err = fs.is_tool_available(vars.CURL, "curl") | ||
132 | if not curl_ok then | ||
133 | return nil, err | ||
134 | end | ||
135 | end | ||
136 | 133 | ||
137 | if not self.config.key then | 134 | if fs.which_tool("downloader") == "wget" then |
138 | return nil, "Must have API key before performing any actions." | 135 | local curl_ok, err = fs.is_tool_available(vars.CURL, "curl") |
139 | end | 136 | if not curl_ok then |
140 | if params and next(params) then | 137 | return nil, err |
141 | url = url .. ("?" .. encode_query_string(params)) | ||
142 | end | ||
143 | local method = "GET" | ||
144 | local out | ||
145 | local tmpfile = fs.tmpname() | ||
146 | if post_params then | ||
147 | method = "POST" | ||
148 | local curl_cmd = vars.CURL.." "..vars.CURLNOCERTFLAG.." -f -L --silent --user-agent \""..cfg.user_agent.." via curl\" " | ||
149 | for k,v in pairs(post_params) do | ||
150 | local var = v | ||
151 | if type(v) == "table" then | ||
152 | var = "@"..v.fname | ||
153 | end | 138 | end |
154 | curl_cmd = curl_cmd .. "--form \""..k.."="..var.."\" " | ||
155 | end | 139 | end |
156 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | 140 | |
157 | curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " | 141 | if not self.config.key then |
142 | return nil, "Must have API key before performing any actions." | ||
158 | end | 143 | end |
159 | local ok = fs.execute_string(curl_cmd..fs.Q(url).." -o "..fs.Q(tmpfile)) | 144 | if params and next(params) then --! |
160 | if not ok then | 145 | url = url .. ("?" .. encode_query_string(params)) |
161 | return nil, "API failure: " .. redact_api_url(url) | ||
162 | end | 146 | end |
163 | else | 147 | local method = "GET" |
164 | local ok, err = fs.download(url, tmpfile) | 148 | local out: string |
165 | if not ok then | 149 | local tmpfile = fs.tmpname() |
166 | return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url) | 150 | if post_params then |
151 | method = "POST" | ||
152 | local curl_cmd = vars.CURL.." "..vars.CURLNOCERTFLAG.." -f -L --silent --user-agent \""..cfg.user_agent.." via curl\" " | ||
153 | for k,v in pairs(post_params) do | ||
154 | local var = v | ||
155 | if v is {string: string} then | ||
156 | var = "@"..v.fname | ||
157 | end | ||
158 | curl_cmd = curl_cmd .. "--form \""..k.."="..var as string.."\" " --! cast | ||
159 | end | ||
160 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
161 | curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " | ||
162 | end | ||
163 | local ok = fs.execute_string(curl_cmd..fs.Q(url).." -o "..fs.Q(tmpfile)) | ||
164 | if not ok then | ||
165 | return nil, "API failure: " .. redact_api_url(url) | ||
166 | end | ||
167 | else | ||
168 | local ok, err = fs.download(url, tmpfile) | ||
169 | if not ok then | ||
170 | return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url) | ||
171 | end | ||
167 | end | 172 | end |
168 | end | ||
169 | 173 | ||
170 | local tmpfd = io.open(tmpfile) | 174 | local tmpfd = io.open(tmpfile) |
171 | if not tmpfd then | 175 | if not tmpfd then |
176 | os.remove(tmpfile) | ||
177 | return nil, "API failure reading temporary file - " .. redact_api_url(url) | ||
178 | end | ||
179 | out = tmpfd:read("*a") | ||
180 | tmpfd:close() | ||
172 | os.remove(tmpfile) | 181 | os.remove(tmpfile) |
173 | return nil, "API failure reading temporary file - " .. redact_api_url(url) | ||
174 | end | ||
175 | out = tmpfd:read("*a") | ||
176 | tmpfd:close() | ||
177 | os.remove(tmpfile) | ||
178 | 182 | ||
179 | if self.debug then | 183 | if self.debug then |
180 | util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ") | 184 | util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ") |
181 | end | 185 | end |
182 | 186 | ||
183 | return json.decode(out) | 187 | return json.decode(out) |
184 | end | 188 | end |
185 | 189 | ||
186 | else -- use LuaSocket and LuaSec | 190 | else -- use LuaSocket and LuaSec |
187 | 191 | ||
188 | local warned_luasec = false | 192 | local warned_luasec = false |
189 | 193 | ||
190 | function Api:request(url, params, post_params) | 194 | function Api:request(url: string, params?: Parameters, post_params?: Parameters): {string : any}, string |
191 | local server = tostring(self.config.server) | 195 | local server = tostring(self.config.server) |
192 | local http_ok, http | 196 | local http_ok, http: boolean, any |
193 | local via = "luasocket" | 197 | local via = "luasocket" |
194 | if server:match("^https://") then | 198 | if server:match("^https://") then |
195 | http_ok, http = pcall(require, "ssl.https") | 199 | http_ok, http = pcall(require, "ssl.https") --! a lot of new files in the src dir |
196 | if http_ok then | 200 | if http_ok then |
197 | via = "luasec" | 201 | via = "luasec" |
198 | else | 202 | else |
199 | if not warned_luasec then | 203 | if not warned_luasec then |
200 | util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.") | 204 | util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.") |
201 | warned_luasec = true | 205 | warned_luasec = true |
206 | end | ||
207 | http_ok, http = pcall(require, "socket.http") | ||
208 | url = url:gsub("^https", "http") | ||
209 | via = "luasocket" | ||
202 | end | 210 | end |
211 | else | ||
203 | http_ok, http = pcall(require, "socket.http") | 212 | http_ok, http = pcall(require, "socket.http") |
204 | url = url:gsub("^https", "http") | ||
205 | via = "luasocket" | ||
206 | end | 213 | end |
207 | else | 214 | if not http_ok then |
208 | http_ok, http = pcall(require, "socket.http") | 215 | return nil, "Failed loading socket library!" |
209 | end | 216 | end |
210 | if not http_ok then | ||
211 | return nil, "Failed loading socket library!" | ||
212 | end | ||
213 | 217 | ||
214 | if not self.config.key then | 218 | if not self.config.key then |
215 | return nil, "Must have API key before performing any actions." | 219 | return nil, "Must have API key before performing any actions." |
216 | end | 220 | end |
217 | local body | 221 | local body: string |
218 | local headers = {} | 222 | local headers: {string: string | integer} = {} |
219 | if params and next(params) then | 223 | if params and next(params) then --! |
220 | url = url .. ("?" .. encode_query_string(params)) | 224 | url = url .. ("?" .. encode_query_string(params)) |
221 | end | 225 | end |
222 | if post_params then | 226 | if post_params then |
223 | local boundary | 227 | local boundary: string |
224 | body, boundary = multipart.encode(post_params) | 228 | body, boundary = multipart.encode(post_params) |
225 | headers["Content-length"] = #body | 229 | headers["Content-length"] = #body |
226 | headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) | 230 | headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) |
227 | end | 231 | end |
228 | local method = post_params and "POST" or "GET" | 232 | local method = post_params and "POST" or "GET" |
229 | if self.debug then | 233 | if self.debug then |
230 | util.printout("[" .. tostring(method) .. " via "..via.."] " .. redact_api_url(url) .. " ... ") | 234 | util.printout("[" .. tostring(method) .. " via "..via.."] " .. redact_api_url(url) .. " ... ") |
231 | end | 235 | end |
232 | local out = {} | 236 | local out = {} |
233 | local _, status = http.request({ | 237 | local _, status = http.request({ |
234 | url = url, | 238 | url = url, |
235 | headers = headers, | 239 | headers = headers, |
236 | method = method, | 240 | method = method, |
237 | sink = ltn12.sink.table(out), | 241 | sink = ltn12.sink.table(out), |
238 | source = body and ltn12.source.string(body) | 242 | source = body and ltn12.source.string(body) |
239 | }) | 243 | }) |
240 | if self.debug then | 244 | if self.debug then |
241 | util.printout(tostring(status)) | 245 | util.printout(tostring(status)) |
242 | end | 246 | end |
243 | local pok, ret, err = pcall(json.decode, table.concat(out)) | 247 | local pok, ret, err = pcall(json.decode, table.concat(out)) |
244 | if pok and ret then | 248 | if pok and ret then |
245 | return ret | 249 | return ret |
250 | end | ||
251 | return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url) | ||
246 | end | 252 | end |
247 | return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url) | ||
248 | end | ||
249 | 253 | ||
250 | end | 254 | end |
251 | 255 | ||
252 | function api.new(args) | 256 | function api.new(args): Api |
253 | local self = {} | 257 | local self = {} |
254 | setmetatable(self, { __index = Api }) | 258 | setmetatable(self, { __index = Api }) |
255 | self.config = self:load_config() or {} | 259 | self.config = self:load_config() or {} |