aboutsummaryrefslogtreecommitdiff
path: root/src/constrain.lua
diff options
context:
space:
mode:
authorAlexander M Pickering <alex@cogarr.net>2024-01-29 16:20:10 -0600
committerAlexander M Pickering <alex@cogarr.net>2024-01-29 16:20:10 -0600
commitc2926c5ec74d7e37da395c8c32a7ff2b4cae7d06 (patch)
treeac2bb208dab1274cdc5e9059ffe014ae19181a4c /src/constrain.lua
downloadfools_rush_in-c2926c5ec74d7e37da395c8c32a7ff2b4cae7d06.tar.gz
fools_rush_in-c2926c5ec74d7e37da395c8c32a7ff2b4cae7d06.tar.bz2
fools_rush_in-c2926c5ec74d7e37da395c8c32a7ff2b4cae7d06.zip
All the files
Diffstat (limited to 'src/constrain.lua')
-rw-r--r--src/constrain.lua109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/constrain.lua b/src/constrain.lua
new file mode 100644
index 0000000..56f3b6a
--- /dev/null
+++ b/src/constrain.lua
@@ -0,0 +1,109 @@
+--[[
+A function that allows adding constraints on tables.
+This works by makeing a shallow copy of the table,
+and setting __index and __newindex metamethods of the (now empty) table,
+that access the appropriate values of the shallow copy after doing checks.
+
+constrain(tbl, ("get " | "set ") + "field name", function(tbl, value)
+ --this function is called with the table ("self") and the
+ --value that is being set or retrived
+end)
+
+This module is just a function that can add constraints on a table. Use
+it like this:
+
+ local constrain = require("constrain")
+ local my_tbl = {}
+ constrain(my_tbl,"get my_field",function(self,value)
+ --Here, self is my_tbl, value will be
+ --"my_field"
+ assert(value > 0 and value < 20, "my_value must be between 0 and 20")
+ end)
+ --From now on, if my_tbl.my_field gets set to anything outside of (0:20),
+ --an error will be thrown.
+
+This function should be totally transparent to the outside.
+]]
+
+local constrained_tables = {}
+
+return function(tbl,trigger,func)
+ local is_empty = true
+ for k,v in pairs(tbl) do
+ is_empty = false
+ break;
+ end
+ local meta = getmetatable(tbl)
+
+ --This table has never had constrain() called on it before,
+ --make the shallow copy with hooks and stuff
+ if constrained_tables[tbl] == nil then
+ --Copy all the variables into a shallow copy
+ local shallow_copy = {}
+ for k,v in pairs(tbl) do
+ shallow_copy[k] = v
+ tbl[k] = nil
+ end
+
+ --Set the shallow copy's metatable to the original table's
+ --metatable
+ setmetatable(shallow_copy,meta)
+
+ --Set the original table's metatable to the hookable thing
+ local t_meta = {}
+ t_meta.get_hooks = {}
+ t_meta.set_hooks = {}
+ t_meta.__index = function(self,key)
+ local ret = shallow_copy[key]
+ for _,hook in pairs(t_meta.get_hooks[key] or {}) do
+ hook(self,ret)
+ end
+ return ret
+ end
+ t_meta.__newindex = function(self,key,value)
+ for _,hook in pairs(t_meta.set_hooks[key] or {}) do
+ hook(self,value)
+ end
+ shallow_copy[key] = value
+ end
+ t_meta.__pairs = function(self)
+ return pairs(shallow_copy)
+ end
+ t_meta.__len = function(self)
+ return #shallow_copy
+ end
+ t_meta.__call = function(self,...)
+ return shallow_copy(...)
+ end
+ t_meta.__mode = meta and meta.__mode or ""
+ t_meta.__gc = meta and meta.__gc
+ t_meta.__metatable = meta
+
+ setmetatable(tbl,t_meta)
+ constrained_tables[tbl] = t_meta
+ end
+
+ --By this point, tbl is a "constrainable" table. we can just
+ --add functios to it's get_hooks and set_hooks to do whatever checking we need
+ --functions added to get_hooks should be
+ -- function(self,value) ... end
+ --functions added to set_hooks should be
+ -- function(self,value) ... end
+ --
+ local getset,field = string.match(trigger,"(get) (.+)")
+ if not getset then
+ getset,field = string.match(trigger,"(set) (.+)")
+ end
+ --local getset,field = string.match(trigger,"(get|set) (.+)")
+ assert(getset,"constrain() must be called with \"get\" or \"set\" as the first word in the pattern")
+ assert(field,"constrain() must specify a field to trigger on")
+ if getset == "get" then
+ local gh = constrained_tables[tbl].get_hooks
+ gh[field] = gh[field] or {}
+ table.insert(gh[field],func)
+ elseif getset == "set" then
+ local sh = constrained_tables[tbl].set_hooks
+ sh[field] = sh[field] or {}
+ table.insert(sh[field],func)
+ end
+end