]]--
local uci = require("uci")
local util = require("luci.util")
-local setmetatable = setmetatable
-local rawget = rawget
-local rawset = rawset
-local error = error
-local tostring = tostring
+local setmetatable, rawget, rawset = setmetatable, rawget, rawset
+local error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
+local table = table
+--- LuCI UCI model library.
module("luci.model.uci", function(m) setmetatable(m, {__index = uci}) end)
-local configs_mt = {}
-local sections_mt = {}
-local options_mt = {}
-
savedir_default = "/tmp/.uci"
confdir_default = "/etc/config"
savedir_state = "/var/state"
-config = {}
-setmetatable(config, configs_mt)
-
--- Level 1 (configs)
-function configs_mt.__index(self, key)
- local node = rawget(self, key)
- if not node then
- if not uci.load(key) then
- return nil
- end
- node = {}
- node[".name"] = key
- setmetatable(node, sections_mt)
- rawset(self, key, node)
+--- Delete all sections of a given type that match certain criteria.
+-- @param config UCI config
+-- @param type UCI section type
+-- @param comparator Function that will be called for each section and
+-- returns a boolean whether to delete the current section (optional)
+function delete_all(config, type, comparator)
+ local del = {}
+ local function helper (section)
+ if not comparator or comparator(section) then
+ table.insert(del, section[".name"])
+ end
end
- return node
-end
-function configs_mt.__newindex()
- error("invalid operation")
-end
-
-
--- Level 2 (sections)
-function sections_mt.__index(self, key)
- local node = rawget(self, key)
- if not node then
- node = {}
- node[".conf"] = self[".name"]
- node[".name"] = key
- node[".type"] = uci.get(self[".name"], key)
- setmetatable(node, options_mt)
- rawset(self, key, node)
+
+ foreach(config, type, helper)
+
+ for i, j in ipairs(del) do
+ delete(config, j)
end
- return node
end
-function sections_mt.__newindex(self, key, value)
- if not value then
- if uci.delete(self[".name"], key) then
- rawset(self, key, nil)
- else
- error("unable to delete section")
- end
- elseif key == "" then
- key = uci.add(self[".name"], tostring(value))
- if key then
- rawset(self, "", self[key])
- else
- error("unable to create section")
- end
+
+--- Create a new section and initialize it with data.
+-- @param config UCI config
+-- @param type UCI section type
+-- @param name UCI section name (optional)
+-- @param values Table of key - value pairs to initialize the section with
+-- @return Name of created section
+function section(config, type, name, values)
+ local stat = true
+ if name then
+ stat = set(config, name, type)
else
- if not uci.set(self[".name"], key, value) then
- error("unable to create section")
- end
+ name = add(config, type)
+ stat = name and true
+ end
+
+ if stat and values then
+ stat = tset(config, name, values)
end
+
+ return stat and name
end
+--- Savely load the configuration.
+-- @param config Configuration to load
+-- @return Sucess status
+-- @see load_state
+-- @see load
+function load_config(...)
+ set_confdir(confdir_default)
+ set_savedir(savedir_default)
+ return load(...)
+end
--- Level 3 (options)
-function options_mt.__index(self, key)
- return uci.get(self[".conf"], self[".name"], key)
+--- Savely load state values.
+-- @param config Configuration to load
+-- @return Sucess status
+-- @see load_config
+-- @see load
+function load_state(config)
+ set_confdir(confdir_default)
+ set_savedir(savedir_state)
+ return load(config)
end
-function options_mt.__newindex(self, key, value)
- if not value then
- if not uci.delete(self[".conf"], self[".name"], key) then
- error("unable to delete option")
- end
- else
- if not uci.set(self[".conf"], self[".name"], key, tostring(value)) then
- error("unable to write option")
+
+--- Save changes to config values.
+-- @param config Configuration to save
+-- @return Sucess status
+-- @see save_state
+-- @see save
+function save_config(config)
+ set_savedir(savedir_default)
+ return save(config)
+end
+
+--- Save changes to state values.
+-- @param config Configuration to save
+-- @return Sucess status
+-- @see save_config
+-- @see save
+function save_state(config)
+ set_savedir(savedir_state)
+ return save(config)
+end
+
+--- Updated the data of a section using data from a table.
+-- @param config UCI config
+-- @param section UCI section name (optional)
+-- @param values Table of key - value pairs to update the section with
+function tset(config, section, values)
+ local stat = true
+ for k, v in pairs(values) do
+ if k:sub(1, 1) ~= "." then
+ stat = stat and set(config, section, k, v)
end
end
+ return stat
end
+
+
+--- Add an anonymous section.
+-- @class function
+-- @name add
+-- @param config UCI config
+-- @param type UCI section type
+-- @return Name of created section
+
+--- Get a table of unsaved changes.
+-- @class function
+-- @name changes
+-- @param config UCI config
+-- @return Table of changes
+
+--- Commit unsaved changes.
+-- @class function
+-- @name commit
+-- @param config UCI config
+-- @return Boolean whether operation succeeded
+-- @see revert
+
+--- Deletes a section or an option.
+-- @class function
+-- @name delete
+-- @param config UCI config
+-- @param section UCI section name
+-- @param option UCI option (optional)
+-- @return Boolean whether operation succeeded
+
+--- Call a function for every section of a certain type.
+-- @class function
+-- @name foreach
+-- @param config UCI config
+-- @param type UCI section type
+-- @param callback Function to be called
+-- @return Boolean whether operation succeeded
+
+--- Get a section type or an option
+-- @class function
+-- @name get
+-- @param config UCI config
+-- @param section UCI section name
+-- @param option UCI option (optional)
+-- @return UCI value
+
+--- Get all sections of a config or all values of a section.
+-- @class function
+-- @name get_all
+-- @param config UCI config
+-- @param section UCI section name (optional)
+-- @return Table of UCI sections or table of UCI values
+
+--- Manually load a config.
+-- Warning: This function is unsave! You should use load_config or load_state if possible.
+-- @class function
+-- @name load
+-- @param config UCI config
+-- @return Boolean whether operation succeeded
+-- @see load_config
+-- @see load_state
+-- @see save
+-- @see unload
+
+--- Revert unsaved changes.
+-- @class function
+-- @name revert
+-- @param config UCI config
+-- @return Boolean whether operation succeeded
+-- @see commit
+
+--- Saves changes made to a config to make them committable.
+-- @class function
+-- @name save
+-- @param config UCI config
+-- @return Boolean whether operation succeeded
+-- @see load
+-- @see unload
+
+--- Set a value or create a named section.
+-- Warning: This function is unsave! You should use save_config or save_state if possible.
+-- @class function
+-- @name set
+-- @param config UCI config
+-- @param section UCI section name
+-- @param option UCI option or UCI section type
+-- @param value UCI value or nil if you want to create a section
+-- @return Boolean whether operation succeeded
+
+--- Set the configuration directory.
+-- @class function
+-- @name set_confdir
+-- @param directory UCI configuration directory
+-- @return Boolean whether operation succeeded
+
+--- Set the directory for uncommited changes.
+-- @class function
+-- @name set_savedir
+-- @param directory UCI changes directory
+-- @return Boolean whether operation succeeded
+
+--- Discard changes made to a config.
+-- @class function
+-- @name unload
+-- @param config UCI config
+-- @return Boolean whether operation succeeded
+-- @see load
+-- @see save