I’m using Lua for configuration. That means I need to validate entries for correctness, provide default values and warning messages where necessary, as well as ensuring my code doesn’t blow up when trying to access a non-existent structure. I’m finding my code becoming bloated with largely boiler-plate safety code, particularly when accessing entries that are nested a few levels deep.
Is there a nice, idiomatic way of handling this?
Regards,
Chris
—
Chris Smith <space.dandy@icloud.com>
Default values in top-level structures are fairly straightforward: prepopulate a table with the defaults and pass it as the third ("env") parameter to loadfile(). This has the advantage that the user's configuration file can read the defaults and perform logic on them if necessary. (But note the caveat below.)
Default values in nested structures can be handled with a function akin to _javascript_'s Object.assign() -- construct a deep copy of a table containing the default values, and use a single generic function that recursively copies keys from the object coming from the configuration file into the default-populated table. (By "recursively", I mean that if the key exists in both tables, and the value is a nested table in both tables, then call the same function using those values.)
One possible technique for validation to consider would be to construct a table with the same layout as the configuration. The values in this table would be validation functions. You can then use a generic, similarly recursive function to iterate over the validation table and the configuration table and run the validator functions on the corresponding values.
Now for the caveat I mentioned: Using plain Lua scripts for configuration can be a security risk if your threat model includes malicious configuration files. If your threat model assumes that authorized administrators are the only ones that can modify the files, then you don't necessarily need to worry about this (although it might be a good idea to put some safeties in place to protect against accidents) but if it's a concern then you'll need to set up a sandbox.
Given the above techniques, your example from your other email might correspond to something like this:
defaults = {
UI = { mainwindow = { size = { width = 640, height = 480 } } }
}
function validateSize(value)
return type(value) == 'table' and type(value.width) == 'number' and type(value.height) == 'number'
end
validators = {
UI = {
mainwindow = {
size = validateSize
}
}
}
/s/ Adam