aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-02 14:48:01 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-05 20:51:31 +0300
commit2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8 (patch)
treebe74689bb9af277471c2a6703cbce675bee5aefe
parent7cf9d20e32276eae933740b69199376c864f541d (diff)
downloadluarocks-2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8.tar.gz
luarocks-2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8.tar.bz2
luarocks-2fe9dd208a3f2f3f1b308102809c4f2cbdd826f8.zip
fun, type_check and api update
-rw-r--r--src/luarocks/fun.tl6
-rw-r--r--src/luarocks/type_check.tl36
-rw-r--r--src/luarocks/upload/api.tl232
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 @@
3local record fun 3local record fun
4end 4end
5 5
6function fun.concat(xs: {any}, ys: {any}): {any} --? not generic becuse lua suports differnt types in one array 6function 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
17end 17end
18 18
19function fun.contains(xs: {any}, v: any): boolean --? same logic as above fine? 19function 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)
56end 56end
57 57
58function fun.reverse_in(t: {any}): {any} 58function 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")
14type_check.MAGIC_PLATFORMS = 0xEBABEFAC 14type_check.MAGIC_PLATFORMS = 0xEBABEFAC
15 15
16do 16do
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
71local function check_version(version: string, typetbl, context: string) 71local 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
81end 81end
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
95local function type_check_item(version, item, typetbl, context) 97local 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
133end 134end
134 135
135local function mkfield(context, field) 136local 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.
167function type_check.type_check_table(version, tbl, typetbl, context) 168function 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
198end 196end
199 197
200function type_check.check_undeclared_globals(globals, typetbl) 198function 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
19end 21end
20 22
23local type Parameters = multipart.Parameters
24
21local function upload_config_file(): string 25local 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
33end 37end
34 38
35function Api:save_config() 39function 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
91end 95end
92 96
93function Api.raw_method(self: Api, path: string, ...) --! path, ... type 97function 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, ...)
97end 101end
98 102
99local function encode_query_string(t, sep) 103local 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)
117end 121end
118 122
119local function redact_api_url(url) 123local 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
122end 126end
123 127
124local ltn12_ok, ltn12 = pcall(require, "ltn12") 128local ltn12_ok, ltn12 = pcall(require, "ltn12")
125if not ltn12_ok then -- If not using LuaSocket and/or LuaSec... 129if not ltn12_ok then -- If not using LuaSocket and/or LuaSec...
126 130
127function 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)
184end 188 end
185 189
186else -- use LuaSocket and LuaSec 190else -- use LuaSocket and LuaSec
187 191
188local warned_luasec = false 192 local warned_luasec = false
189 193
190function 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)
248end
249 253
250end 254end
251 255
252function api.new(args) 256function 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 {}