-- WirePlumber -- -- Copyright © 2022 Collabora Ltd. -- -- SPDX-License-Identifier: MIT -- -- Settings manager helper -- -- This sets up a lua table that will automatically populate itself with -- values from the settings, falling back to the default values specified. -- Any changes in the settings will automatically change the values of this -- table. -- -- Usage: -- -- local settings_manager = require ("settings-manager") -- local defaults = { -- ["foo"] = true, -- ["foo-bar"] = 3, -- } -- local settings = settings_manager.new ("example.prefix.", defaults) -- -- The above "settings" table should now look like this internally: -- { -- foo = true, -- or the value specified in the Settings -- foo_bar = 3, -- or the value specified in the Settings -- } -- -- Additionally, a "subscribe" method is present in the "settings" table, which -- allows subscribing to changes of the values: -- -- settings:subscribe ("foo-bar", function (new_value) -- Log.info ("foo-bar value changed to " .. new_value) -- end) -- local settings_manager = {} local private_api = {} function private_api.subscribe (self, key, closure) if not self.subscribers[key] then self.subscribers[key] = {} end table.insert (self.subscribers, closure) end function private_api.call_subscribers (self, key, new_value) if self.subscribers[key] then for i, closure in ipairs (self.subscribers[key]) do closure (new_value) end end end function private_api.update_value (self, key, json_value, default) local new_value = json_value and json_value:parse () or default -- only accept values that have the same type as the default value if type (new_value) ~= type (default) then new_value = default end -- store only if the new value is not equal to the default self.values[key] = (new_value ~= default) and new_value or nil return new_value end function settings_manager.new (_prefix, _defaults) -- private storage table local private = { prefix = _prefix, prefix_len = string.len (_prefix), defaults = _defaults, values = {}, subscribers = {}, } setmetatable (private, { __index = private_api }) -- initialize with the values found in Settings for key, default in pairs (private.defaults) do local json_value = Settings.get (private.prefix .. key) private:update_value (key, json_value, default) end -- subscribe for changes in Settings Settings.subscribe (private.prefix .. "*", function (_, setting, json_value) local key = string.sub (setting, private.prefix_len, -1) local default = private.defaults[key] -- unknown key, ignore it if default == nil then return end local new_value = private:update_value (key, json_value, default) private:call_subscribers (key, new_value) end) -- return an empty table with a metatable that will resolve -- keys to the values stored in `private` or their default values return setmetatable ({}, { __private = private, __index = function (self, key) if key == "subscribe" then -- special case, "subscribe" is a method return function (self, key, closure) local private = getmetatable (self) ["__private"] private:subscribe (key, closure) end else local private = getmetatable (self) ["__private"] key = string.gsub (key, "_", "-") -- foo_bar_baz -> foo-bar-baz local value = private.values[key] return (value ~= nil) and value or private.defaults[key] end end, __newindex = function(_, key, _) error ('Not allowed to modify configuration value') end }) end return settings_manager