-
-
Notifications
You must be signed in to change notification settings - Fork 190
/
Copy pathschema.lua
151 lines (140 loc) · 4.38 KB
/
schema.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
---@class CodeCompanion.Schema
---@field default any The default value of the item
---@field type "string"|"number"|"integer"|"boolean"|"enum"|"list"|"map"
---@field mapping? string Where to map the item to the request
---@field order nil|integer The order to display the item when the full schema is shown
---@field optional nil|boolean
---@field choices nil|table|fun(self: CodeCompanion.Adapter): table<string>
---@field desc string The description of the schema item
---@field condition? nil|fun(self: CodeCompanion.Adapter): boolean
---@field validate? fun(value: any): boolean, nil|string
local M = {}
local islist = vim.islist or vim.tbl_islist
---Return the default values for a schema
---@param adapter CodeCompanion.Adapter
---@param defaults? table Any default values to use (will override schema defaults)
M.get_default = function(adapter, defaults)
local schema = adapter.schema
local ret = {}
for k, v in pairs(schema) do
if type(v.condition) == "function" and not v.condition(adapter) then
goto continue
end
if not vim.startswith(k, "_") then
if defaults and defaults[k] ~= nil then
ret[k] = defaults[k]
else
-- Use the default value in the schema
ret[k] = v.default
end
end
::continue::
end
return ret
end
---@param schema CodeCompanion.Schema
---@param value any
---@param adapter CodeCompanion.Adapter
---@return boolean
---@return nil|string
local function validate_type(schema, value, adapter)
local ptype = schema.type or "string"
if value == nil then
return schema.optional
elseif ptype == "enum" then
local choices = schema.choices
if type(choices) == "function" then
choices = choices(adapter)
end
local valid = vim.tbl_contains(choices, value)
if not valid and choices then
return valid, string.format("must be one of %s", table.concat(choices, ", "))
else
return valid
end
elseif ptype == "list" then
-- TODO validate subtype
return type(value) == "table" and islist(value)
elseif ptype == "map" then
local valid = type(value) == "table" and (vim.tbl_isempty(value) or not islist(value))
-- TODO validate subtype and subtype_key
if valid then
-- Hack to make sure empty dicts get serialized properly
setmetatable(value, vim._empty_dict_mt)
end
return valid
elseif ptype == "number" then
return type(value) == "number"
elseif ptype == "integer" then
return type(value) == "number" and math.floor(value) == value
elseif ptype == "boolean" then
return type(value) == "boolean"
elseif ptype == "string" then
return true
else
error(string.format("Unknown param type '%s'", ptype))
end
end
---@param schema CodeCompanion.Schema
---@param value any
---@param adapter CodeCompanion.Adapter
---@return boolean
---@return nil|string
local function validate_field(schema, value, adapter)
local valid, err = validate_type(schema, value, adapter)
if not valid then
return valid, err
end
if schema.validate and value ~= nil then
return schema.validate(value)
end
return true
end
---@param schema CodeCompanion.Schema
---@param values table
---@param adapter CodeCompanion.Adapter
---@return nil|table<string, string>
M.validate = function(schema, values, adapter)
local errors = {}
for k, v in pairs(schema) do
local valid, err = validate_field(v, values[k], adapter)
if not valid then
errors[k] = err or string.format("Not a valid %s", v.type)
end
end
if not vim.tbl_isempty(errors) then
return errors
end
end
---Order the keys of the schema
---@param adapter CodeCompanion.Adapter
---@return string[]
M.get_ordered_keys = function(adapter)
local schema = adapter.schema
for k, v in pairs(schema) do
if type(v.condition) == "function" and not v.condition(adapter) then
schema[k] = nil
end
end
local keys = vim.tbl_keys(schema)
-- Sort the params by required, then if they have no value, then by name
table.sort(keys, function(a, b)
local aparam = schema[a]
local bparam = schema[b]
if aparam.order then
if not bparam.order then
return true
elseif aparam.order ~= bparam.order then
return aparam.order < bparam.order
end
elseif bparam.order then
return false
end
if (aparam.optional == true) ~= (bparam.optional == true) then
return bparam.optional
end
return a < b
end)
return keys
end
return M