diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-08-22 17:48:57 -0300 |
---|---|---|
committer | Hisham Muhammad <hisham@gobolinux.org> | 2024-10-21 13:30:51 -0300 |
commit | 68a5ee0d17a366cd49c4fa9ff6ac887fe917acd8 (patch) | |
tree | 71e70b9534f3da3197ed12249d797ff71956deb7 /src | |
parent | f2697b158f0b94f615dffee553ac18cfbd72cd52 (diff) | |
download | luarocks-68a5ee0d17a366cd49c4fa9ff6ac887fe917acd8.tar.gz luarocks-68a5ee0d17a366cd49c4fa9ff6ac887fe917acd8.tar.bz2 luarocks-68a5ee0d17a366cd49c4fa9ff6ac887fe917acd8.zip |
Teal: convert luarocks.type_check
Diffstat (limited to 'src')
-rw-r--r-- | src/luarocks/type_check.tl (renamed from src/luarocks/type_check.lua) | 100 |
1 files changed, 62 insertions, 38 deletions
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.tl index 21085ef9..1b332be0 100644 --- a/src/luarocks/type_check.lua +++ b/src/luarocks/type_check.tl | |||
@@ -1,20 +1,42 @@ | |||
1 | 1 | ||
2 | local type_check = {} | 2 | local record type_check |
3 | type_check_table: function(version: string, tbl: {any: any}, typetbl: TableSchema, context: string): boolean, string --! tbl and typetbl types) | ||
4 | |||
5 | record TableSchema | ||
6 | fields: {string: TableSchema} | ||
7 | _type: string | ||
8 | _name: string | ||
9 | _pattern: string | ||
10 | _version: string | ||
11 | _more: boolean | ||
12 | _mandatory: boolean | ||
13 | _any: TableSchema | ||
14 | end | ||
15 | |||
16 | MAGIC_PLATFORMS: TableSchema | ||
17 | end | ||
3 | 18 | ||
4 | local cfg = require("luarocks.core.cfg") | 19 | local cfg = require("luarocks.core.cfg") |
5 | local fun = require("luarocks.fun") | 20 | local fun = require("luarocks.fun") |
6 | local util = require("luarocks.util") | 21 | local util = require("luarocks.util") |
7 | local vers = require("luarocks.core.vers") | 22 | local vers = require("luarocks.core.vers") |
23 | |||
24 | local type TableSchema = type_check.TableSchema | ||
8 | -------------------------------------------------------------------------------- | 25 | -------------------------------------------------------------------------------- |
9 | 26 | ||
10 | -- A magic constant that is not used anywhere in a schema definition | 27 | -- A magic constant that is not used anywhere in a schema definition |
11 | -- and retains equality when the table is deep-copied. | 28 | -- and retains equality when the table is deep-copied. |
12 | type_check.MAGIC_PLATFORMS = 0xEBABEFAC | 29 | type_check.MAGIC_PLATFORMS = {} |
13 | 30 | ||
14 | do | 31 | do |
15 | local function fill_in_version(tbl, version) | 32 | local function fill_in_version(tbl: TableSchema, version?: string) |
16 | for _, v in pairs(tbl) do | 33 | |
17 | if type(v) == "table" then | 34 | if not tbl.fields then --! validation |
35 | return | ||
36 | end | ||
37 | |||
38 | for _, v in pairs(tbl.fields) do | ||
39 | if v is TableSchema then | ||
18 | if v._version == nil then | 40 | if v._version == nil then |
19 | v._version = version | 41 | v._version = version |
20 | end | 42 | end |
@@ -23,14 +45,13 @@ do | |||
23 | end | 45 | end |
24 | end | 46 | end |
25 | 47 | ||
26 | local function expand_magic_platforms(tbl) | 48 | local function expand_magic_platforms(tbl: TableSchema) |
27 | for k,v in pairs(tbl) do | 49 | for k,v in pairs(tbl.fields) do |
28 | if v == type_check.MAGIC_PLATFORMS then | 50 | if v == type_check.MAGIC_PLATFORMS then |
29 | tbl[k] = { | 51 | tbl.fields[k] = { |
30 | _any = util.deep_copy(tbl) | 52 | _any = util.deep_copy(tbl as {string: any}) as TableSchema |
31 | } | 53 | } |
32 | tbl[k]._any[k] = nil | 54 | tbl.fields[k]._any.fields[k] = nil |
33 | elseif type(v) == "table" then | ||
34 | expand_magic_platforms(v) | 55 | expand_magic_platforms(v) |
35 | end | 56 | end |
36 | end | 57 | end |
@@ -41,17 +62,17 @@ do | |||
41 | -- and the value is a schema specification. Schema versions are considered | 62 | -- and the value is a schema specification. Schema versions are considered |
42 | -- incremental: version "2.0" only needs to specify what's new/changed from | 63 | -- incremental: version "2.0" only needs to specify what's new/changed from |
43 | -- version "1.0". | 64 | -- version "1.0". |
44 | function type_check.declare_schemas(inputs) | 65 | function type_check.declare_schemas(inputs: {string: TableSchema}): {string : TableSchema}, {string} |
45 | local schemas = {} | 66 | local schemas: {string: TableSchema} = {} |
46 | local parent_version | 67 | local parent_version: string |
47 | 68 | ||
48 | local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions)) | 69 | local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions)) |
49 | 70 | ||
50 | for _, version in ipairs(versions) do | 71 | for _, version in ipairs(versions) do |
51 | local schema = inputs[version] | 72 | local schema = inputs[version] |
52 | if parent_version ~= nil then | 73 | if parent_version then |
53 | local copy = util.deep_copy(schemas[parent_version]) | 74 | local copy: TableSchema = util.deep_copy(schemas[parent_version] as {any: any}) as TableSchema |
54 | util.deep_merge(copy, schema) | 75 | util.deep_merge(copy as {any: any}, schema as {any: any}) |
55 | schema = copy | 76 | schema = copy |
56 | end | 77 | end |
57 | fill_in_version(schema, version) | 78 | fill_in_version(schema, version) |
@@ -66,7 +87,7 @@ end | |||
66 | 87 | ||
67 | -------------------------------------------------------------------------------- | 88 | -------------------------------------------------------------------------------- |
68 | 89 | ||
69 | local function check_version(version, typetbl, context) | 90 | local function check_version(version: string, typetbl: TableSchema, context: string): boolean, string |
70 | local typetbl_version = typetbl._version or "1.0" | 91 | local typetbl_version = typetbl._version or "1.0" |
71 | if vers.compare_versions(typetbl_version, version) then | 92 | if vers.compare_versions(typetbl_version, version) then |
72 | if context == "" then | 93 | if context == "" then |
@@ -78,6 +99,8 @@ local function check_version(version, typetbl, context) | |||
78 | return true | 99 | return true |
79 | end | 100 | end |
80 | 101 | ||
102 | |||
103 | |||
81 | --- Type check an object. | 104 | --- Type check an object. |
82 | -- The object is compared against an archetypical value | 105 | -- The object is compared against an archetypical value |
83 | -- matching the expected type -- the actual values don't matter, | 106 | -- matching the expected type -- the actual values don't matter, |
@@ -90,8 +113,7 @@ end | |||
90 | -- @return boolean or (nil, string): true if type checking | 113 | -- @return boolean or (nil, string): true if type checking |
91 | -- succeeded, or nil and an error message if it failed. | 114 | -- succeeded, or nil and an error message if it failed. |
92 | -- @see type_check_table | 115 | -- @see type_check_table |
93 | local function type_check_item(version, item, typetbl, context) | 116 | local function type_check_item(version: string, item: any, typetbl: TableSchema, context: string): boolean, string |
94 | assert(type(version) == "string") | ||
95 | 117 | ||
96 | if typetbl._version and typetbl._version ~= "1.0" then | 118 | if typetbl._version and typetbl._version ~= "1.0" then |
97 | local ok, err = check_version(version, typetbl, context) | 119 | local ok, err = check_version(version, typetbl, context) |
@@ -100,7 +122,6 @@ local function type_check_item(version, item, typetbl, context) | |||
100 | end | 122 | end |
101 | end | 123 | end |
102 | 124 | ||
103 | local item_type = type(item) or "nil" | ||
104 | local expected_type = typetbl._type or "table" | 125 | local expected_type = typetbl._type or "table" |
105 | 126 | ||
106 | if expected_type == "number" then | 127 | if expected_type == "number" then |
@@ -108,32 +129,32 @@ local function type_check_item(version, item, typetbl, context) | |||
108 | return nil, "Type mismatch on field "..context..": expected a number" | 129 | return nil, "Type mismatch on field "..context..": expected a number" |
109 | end | 130 | end |
110 | elseif expected_type == "string" then | 131 | elseif expected_type == "string" then |
111 | if item_type ~= "string" then | 132 | if not item is string then |
112 | return nil, "Type mismatch on field "..context..": expected a string, got "..item_type | 133 | return nil, "Type mismatch on field "..context..": expected a string, got "..type(item) |
113 | end | 134 | end |
114 | local pattern = typetbl._pattern | 135 | local pattern = typetbl._pattern |
115 | if pattern then | 136 | if pattern then |
116 | if not item:match("^"..pattern.."$") then | 137 | if not item:match("^"..pattern.."$") then --! cast or tostring |
117 | local what = typetbl._name or ("'"..pattern.."'") | 138 | local what = typetbl._name or ("'"..pattern.."'") |
118 | return nil, "Type mismatch on field "..context..": invalid value '"..item.."' does not match " .. what | 139 | return nil, "Type mismatch on field "..context..": invalid value '"..item.."' does not match " .. what |
119 | end | 140 | end |
120 | end | 141 | end |
121 | elseif expected_type == "table" then | 142 | elseif expected_type == "table" then |
122 | if item_type ~= expected_type then | 143 | if not item is table then |
123 | return nil, "Type mismatch on field "..context..": expected a table" | 144 | return nil, "Type mismatch on field "..context..": expected a table" |
124 | else | 145 | else |
125 | return type_check.type_check_table(version, item, typetbl, context) | 146 | return type_check.type_check_table(version, item, typetbl, context) |
126 | end | 147 | end |
127 | elseif item_type ~= expected_type then | 148 | elseif type(item) ~= expected_type then |
128 | return nil, "Type mismatch on field "..context..": expected "..expected_type | 149 | return nil, "Type mismatch on field "..context..": expected "..expected_type |
129 | end | 150 | end |
130 | return true | 151 | return true |
131 | end | 152 | end |
132 | 153 | ||
133 | local function mkfield(context, field) | 154 | local function mkfield(context: string, field: any): string |
134 | if context == "" then | 155 | if context == "" then |
135 | return tostring(field) | 156 | return tostring(field) |
136 | elseif type(field) == "string" then | 157 | elseif field is string then |
137 | return context.."."..field | 158 | return context.."."..field |
138 | else | 159 | else |
139 | return context.."["..tostring(field).."]" | 160 | return context.."["..tostring(field).."]" |
@@ -162,30 +183,33 @@ end | |||
162 | -- to be used by error messages. | 183 | -- to be used by error messages. |
163 | -- @return boolean or (nil, string): true if type checking | 184 | -- @return boolean or (nil, string): true if type checking |
164 | -- succeeded, or nil and an error message if it failed. | 185 | -- succeeded, or nil and an error message if it failed. |
165 | function type_check.type_check_table(version, tbl, typetbl, context) | 186 | function type_check.type_check_table(version: string, tbl: {any: any}, typetbl: TableSchema, context: string): boolean, string |
166 | assert(type(version) == "string") | ||
167 | assert(type(tbl) == "table") | ||
168 | assert(type(typetbl) == "table") | ||
169 | 187 | ||
170 | local ok, err = check_version(version, typetbl, context) | 188 | local ok, err = check_version(version, typetbl, context) |
171 | if not ok then | 189 | if not ok then |
172 | return nil, err | 190 | return nil, err |
173 | end | 191 | end |
174 | 192 | ||
193 | if not typetbl.fields then --! validation | ||
194 | -- if there are no fields, exit the checker | ||
195 | return true | ||
196 | end | ||
197 | |||
175 | for k, v in pairs(tbl) do | 198 | for k, v in pairs(tbl) do |
176 | local t = typetbl[k] or typetbl._any | 199 | local t = typetbl.fields[tostring(k)] or typetbl._any |
177 | if t then | 200 | if t then |
178 | local ok, err = type_check_item(version, v, t, mkfield(context, k)) | 201 | ok, err = type_check_item(version, v, t, mkfield(context, k)) |
179 | if not ok then return nil, err end | 202 | if not ok then return nil, err end |
180 | elseif typetbl._more then | 203 | elseif typetbl._more then |
181 | -- Accept unknown field | 204 | -- Accept unknown field |
182 | else | 205 | else |
183 | if not cfg.accept_unknown_fields then | 206 | if not cfg.accept_unknown_fields then |
184 | return nil, "Unknown field "..k | 207 | return nil, "Unknown field "..tostring(k) |
185 | end | 208 | end |
186 | end | 209 | end |
187 | end | 210 | end |
188 | for k, v in pairs(typetbl) do | 211 | |
212 | for k, v in pairs(typetbl.fields) do | ||
189 | if k:sub(1,1) ~= "_" and v._mandatory then | 213 | if k:sub(1,1) ~= "_" and v._mandatory then |
190 | if not tbl[k] then | 214 | if not tbl[k] then |
191 | return nil, "Mandatory field "..mkfield(context, k).." is missing." | 215 | return nil, "Mandatory field "..mkfield(context, k).." is missing." |
@@ -195,10 +219,10 @@ function type_check.type_check_table(version, tbl, typetbl, context) | |||
195 | return true | 219 | return true |
196 | end | 220 | end |
197 | 221 | ||
198 | function type_check.check_undeclared_globals(globals, typetbl) | 222 | function type_check.check_undeclared_globals(globals: {string: any}, typetbl: TableSchema): boolean, string --! tbl and typetbl types |
199 | local undeclared = {} | 223 | local undeclared = {} |
200 | for glob, _ in pairs(globals) do | 224 | for glob, _ in pairs(globals) do |
201 | if not (typetbl[glob] or typetbl["MUST_"..glob]) then | 225 | if not (typetbl.fields[glob] or typetbl.fields["MUST_"..glob]) then |
202 | table.insert(undeclared, glob) | 226 | table.insert(undeclared, glob) |
203 | end | 227 | end |
204 | end | 228 | end |