aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-07-30 19:24:01 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-05 20:51:31 +0300
commita94eeb7279af61565d55cf446a286b0b9f2c1fe3 (patch)
tree59dbe15bbc6602763dc8fdb75fd492e4c508776c
parenta50299e1d6b280f5ca635b2c7ac9fe889befd6d6 (diff)
downloadluarocks-a94eeb7279af61565d55cf446a286b0b9f2c1fe3.tar.gz
luarocks-a94eeb7279af61565d55cf446a286b0b9f2c1fe3.tar.bz2
luarocks-a94eeb7279af61565d55cf446a286b0b9f2c1fe3.zip
results, queries in progress
-rw-r--r--src/luarocks/core/cfg.d.tl4
-rw-r--r--src/luarocks/queries.tl234
-rw-r--r--src/luarocks/results.tl30
3 files changed, 256 insertions, 12 deletions
diff --git a/src/luarocks/core/cfg.d.tl b/src/luarocks/core/cfg.d.tl
index fccfe0e3..eff86971 100644
--- a/src/luarocks/core/cfg.d.tl
+++ b/src/luarocks/core/cfg.d.tl
@@ -54,7 +54,7 @@ local record cfg
54 record cache 54 record cache
55 luajit_version_checked: boolean 55 luajit_version_checked: boolean
56 luajit_version: string 56 luajit_version: string
57 rocks_provided: {string: string} --? right type? infered from util 57 rocks_provided: {string: string} --? right type? infered from src/luarocks/util
58 end 58 end
59 59
60 record variables 60 record variables
@@ -64,6 +64,8 @@ local record cfg
64 rocks_provided: {Rockspec} 64 rocks_provided: {Rockspec}
65 -- persist 65 -- persist
66 home: string 66 home: string
67 -- queries
68 arch: string
67end 69end
68 70
69return cfg \ No newline at end of file 71return cfg \ No newline at end of file
diff --git a/src/luarocks/queries.tl b/src/luarocks/queries.tl
new file mode 100644
index 00000000..b5996dbe
--- /dev/null
+++ b/src/luarocks/queries.tl
@@ -0,0 +1,234 @@
1
2local record queries
3 record Constraint
4 version: vers.Version
5 op: string
6 no_upgrade: boolean
7 end
8
9 record Query
10 name: string
11 namespace: string
12 constraints: {Constraint}
13 substring: boolean
14 arch: {string: boolean}
15 end
16end
17
18local vers = require("luarocks.core.vers")
19local util = require("luarocks.util")
20local cfg = require("luarocks.core.cfg")
21
22-- local type Config = cfg
23
24local type Query = queries.Query
25local type Constraint = queries.Constraint
26
27local query_mt: metatable<Query> = {}
28
29query_mt.__index = query_mt
30
31function query_mt.type()
32 return "query"
33end
34
35-- Fallback default value for the `arch` field, if not explicitly set.
36query_mt.arch = {
37 src = true,
38 all = true,
39 rockspec = true,
40 installed = true,
41 -- [cfg.arch] = true, -- this is set later
42}
43
44-- Fallback default value for the `substring` field, if not explicitly set.
45query_mt.substring = false
46
47--- Convert the arch field of a query table to table format.
48-- @param input string, table or nil
49local function arch_to_table(input: string | {string: boolean}): {string: boolean}
50 if input is {string: boolean} then
51 return input
52 elseif input is string then
53 local arch = {}
54 for a in input:gmatch("[%w_-]+") do
55 arch[a] = true
56 end
57 return arch
58 end
59end
60
61--- Prepare a query in dependency table format.
62-- @param name string: the package name.
63-- @param namespace string?: the package namespace.
64-- @param version string?: the package version.
65-- @param substring boolean?: match substrings of the name
66-- (default is false, match full name)
67-- @param arch string?: a string with pipe-separated accepted arch values
68-- @param operator string?: operator for version matching (default is "==")
69-- @return table: A query in table format
70function queries.new(name: string, namespace?: string, version?: string, substring?: boolean, arch?: string, operator?: string): Query
71 -- assert(type(namespace) == "string" or not namespace) --! optional parameters?
72 -- assert(type(version) == "string" or not version)
73 -- assert(type(substring) == "boolean" or not substring)
74 -- assert(type(arch) == "string" or not arch)
75 -- assert(type(operator) == "string" or not operator)
76
77 operator = operator or "=="
78
79 local self: Query = {
80 name = name,
81 namespace = namespace,
82 constraints = {},
83 substring = substring,
84 arch = arch_to_table(arch),
85 }
86 if version then
87 table.insert(self.constraints, { op = operator, version = vers.parse_version(version)})
88 end
89
90 query_mt.arch[cfg.arch] = true
91 return setmetatable(self, query_mt)
92end
93
94-- Query for all packages
95-- @param arch string (optional)
96function queries.all(arch?: string): Query
97 -- assert(type(arch) == "string" or not arch) --! optional
98
99 return queries.new("", nil, nil, true, arch)
100end
101
102do
103 local parse_constraints: function(string): {Constraint}, string
104 do
105 local parse_constraint: function(string): Constraint, string
106 do
107 local operators: {string: string} = {
108 ["=="] = "==",
109 ["~="] = "~=",
110 [">"] = ">",
111 ["<"] = "<",
112 [">="] = ">=",
113 ["<="] = "<=",
114 ["~>"] = "~>",
115 -- plus some convenience translations
116 [""] = "==",
117 ["="] = "==",
118 ["!="] = "~="
119 }
120
121 --- Consumes a constraint from a string, converting it to table format.
122 -- For example, a string ">= 1.0, > 2.0" is converted to a table in the
123 -- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned
124 -- back to the caller.
125 -- @param input string: A list of constraints in string format.
126 -- @return (table, string) or nil: A table representing the same
127 -- constraints and the string with the unused input, or nil if the
128 -- input string is invalid.
129 parse_constraint = function(input: string): {string: any}, string
130
131 local no_upgrade, op, versionstr, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
132 local _op = operators[op]
133 local version = vers.parse_version(versionstr)
134 if not _op then
135 return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'"
136 end
137 if not version then
138 return nil, "Could not parse version from constraint: '"..input.."'"
139 end
140 return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest --? false instead of nil
141 end
142 end
143
144 --- Convert a list of constraints from string to table format.
145 -- For example, a string ">= 1.0, < 2.0" is converted to a table in the format
146 -- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}.
147 -- Version tables use a metatable allowing later comparison through
148 -- relational operators.
149 -- @param input string: A list of constraints in string format.
150 -- @return table or nil: A table representing the same constraints,
151 -- or nil if the input string is invalid.
152 parse_constraints = function(input: string): {Constraint}, string
153
154 local constraints, oinput = {}, input
155 local constraint: Constraint
156 while #input > 0 do
157 constraint, input = parse_constraint(input)
158 if constraint then
159 table.insert(constraints, constraint)
160 else
161 return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input
162 end
163 end
164 return constraints
165 end
166 end
167
168 --- Prepare a query in dependency table format.
169 -- @param depstr string: A dependency in string format
170 -- as entered in rockspec files.
171 -- @return table: A query in table format, or nil and an error message in case of errors.
172 function queries.from_dep_string(depstr: string): Query, string
173
174 local ns_name, rest = depstr:match("^%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)")
175 if not ns_name then
176 return nil, "failed to extract dependency name from '"..depstr.."'"
177 end
178
179 ns_name = ns_name:lower()
180
181 local constraints, err = parse_constraints(rest)
182 if not constraints then
183 return nil, err
184 end
185
186 local name, namespace = util.split_namespace(ns_name)
187
188 local self = {
189 name = name,
190 namespace = namespace,
191 constraints = constraints,
192 }
193
194 query_mt.arch[cfg.arch] = true
195 return setmetatable(self, query_mt)
196 end
197end
198
199function queries.from_persisted_table(tbl: Query): Query
200 query_mt.arch[cfg.arch] = true
201 return setmetatable(tbl, query_mt)
202end
203
204--- Build a string representation of a query package name.
205-- Includes namespace, name and version, but not arch or constraints.
206-- @param query table: a query table
207-- @return string: a result such as `my_user/my_rock 1.0` or `my_rock`.
208function query_mt.__tostring(self: Query): string
209 -- function query_mt:__tostring(): string
210 local out = {}
211 if self.namespace then
212 table.insert(out, self.namespace)
213 table.insert(out, "/")
214 end
215 table.insert(out, self.name)
216
217 if #self.constraints > 0 then
218 local pretty = {}
219 for _, c in ipairs(self.constraints) do
220 local v = c.version.string
221 if c.op == "==" then
222 table.insert(pretty, v)
223 else
224 table.insert(pretty, c.op .. " " .. v)
225 end
226 end
227 table.insert(out, " ")
228 table.insert(out, table.concat(pretty, ", "))
229 end
230
231 return table.concat(out)
232end
233
234return queries --! src/luarocks/queries.tl:32 \ No newline at end of file
diff --git a/src/luarocks/results.tl b/src/luarocks/results.tl
index a5bc25fe..7417db7a 100644
--- a/src/luarocks/results.tl
+++ b/src/luarocks/results.tl
@@ -1,10 +1,19 @@
1local record results 1local record results
2 record Results --? name
3 name: string
4 version: string
5 namespace: string
6 arch: string
7 repo: string
8 end
2end 9end
3 10
4local vers = require("luarocks.core.vers") 11local vers = require("luarocks.core.vers")
5local util = require("luarocks.util") 12local util = require("luarocks.util")
6 13
7local result_mt: metatable<any> = {} 14local type Results = results.Results
15
16local result_mt: metatable<Results> = {}
8 17
9result_mt.__index = result_mt 18result_mt.__index = result_mt
10 19
@@ -12,18 +21,18 @@ function result_mt.type()
12 return "result" 21 return "result"
13end 22end
14 23
15function results.new(name, version, repo, arch, namespace) 24function results.new(name: string, version: string, repo: string, arch?: string, namespace?: string): Results, boolean
16 assert(type(name) == "string" and not name:match("/")) 25
17 assert(type(version) == "string") 26 assert(not name:match("/"))
18 assert(type(repo) == "string") 27 -- assert(type(arch) == "string" or not arch) --! arch?: string
19 assert(type(arch) == "string" or not arch) 28 -- assert(type(namespace) == "string" or not namespace) --! namespace?: string
20 assert(type(namespace) == "string" or not namespace) 29
21 30
22 if not namespace then 31 if not namespace then
23 name, namespace = util.split_namespace(name) 32 name, namespace = util.split_namespace(name)
24 end 33 end
25 34
26 local self = { 35 local self: Results = {
27 name = name, 36 name = name,
28 version = version, 37 version = version,
29 namespace = namespace, 38 namespace = namespace,
@@ -41,7 +50,7 @@ end
41-- @param query table: A query in dependency table format. 50-- @param query table: A query in dependency table format.
42-- @param name string: A package name. 51-- @param name string: A package name.
43-- @return boolean: True if names match, false otherwise. 52-- @return boolean: True if names match, false otherwise.
44local function match_name(query, name) 53local function match_name(query: query, name: string): boolean
45 if query.substring then 54 if query.substring then
46 return name:find(query.name, 0, true) and true or false 55 return name:find(query.name, 0, true) and true or false
47 else 56 else
@@ -52,8 +61,7 @@ end
52--- Returns true if the result satisfies a given query. 61--- Returns true if the result satisfies a given query.
53-- @param query: a query. 62-- @param query: a query.
54-- @return boolean. 63-- @return boolean.
55function result_mt:satisfies(query) 64function result_mt:satisfies(query: query): Results, boolean
56 assert(query:type() == "query")
57 return match_name(query, self.name) 65 return match_name(query, self.name)
58 and (query.arch[self.arch] or query.arch["any"]) 66 and (query.arch[self.arch] or query.arch["any"])
59 and ((not query.namespace) or (query.namespace == self.namespace)) 67 and ((not query.namespace) or (query.namespace == self.namespace))