diff --git a/lib/vcomponent.lua b/lib/vcomponent.lua new file mode 100644 index 0000000..4dbbe1c --- /dev/null +++ b/lib/vcomponent.lua @@ -0,0 +1,197 @@ +local proxylist = {} +local proxyobjs = {} +local typelist = {} +local doclist = {} + +local oproxy = component.proxy +function component.proxy(address) + checkArg(1,address,"string") + if proxyobjs[address] ~= nil then + return proxyobjs[address] + end + return oproxy(address) +end + +local olist = component.list +function component.list(filter, exact) + checkArg(1,filter,"string","nil") + local result = {} + local data = {} + for k,v in olist(filter, exact) do + data[#data + 1] = k + data[#data + 1] = v + result[k] = v + end + for k,v in pairs(typelist) do + if filter == nil or (exact and v == filter) or (not exact and v:find(filter, nil, true)) then + data[#data + 1] = k + data[#data + 1] = v + result[k] = v + end + end + local place = 1 + return setmetatable(result, + {__call=function() + local addr,type = data[place], data[place + 1] + place = place + 2 + return addr, type + end} + ) +end + +local otype = component.type +function component.type(address) + checkArg(1,address,"string") + if typelist[address] ~= nil then + return typelist[address] + end + return otype(address) +end + +local odoc = component.doc +function component.doc(address, method) + checkArg(1,address,"string") + checkArg(2,method,"string") + if proxylist[address] ~= nil then + if proxylist[address][method] == nil then + error("no such method",2) + end + if doclist[address] ~= nil then + return doclist[address][method] + end + return nil + end + return odoc(address, method) +end + +local oslot = component.slot +function component.slot(address) + checkArg(1,address,"string") + if proxylist[address] ~= nil then + return -1 -- vcomponents do not exist in a slot + end + return oslot(address) +end + +local omethods = component.methods +function component.methods(address) + checkArg(1,address,"string") + if proxylist[address] ~= nil then + local methods = {} + for k,v in pairs(proxylist[address]) do + if type(v) == "function" then + methods[k] = true -- All vcomponent methods are direct + end + end + return methods + end + return omethods(address) +end + +local oinvoke = component.invoke +function component.invoke(address, method, ...) + checkArg(1,address,"string") + checkArg(2,method,"string") + if proxylist[address] ~= nil then + if proxylist[address][method] == nil then + error("no such method",2) + end + return proxylist[address][method](...) + end + return oinvoke(address, method, ...) +end + +local ofields = component.fields +function component.fields(address) + checkArg(1,address,"string") + if proxylist[address] ~= nil then + return {} -- What even is this? + end + return ofields(address) +end + +local componentCallback = +{ + __call = function(self, ...) return proxylist[self.address][self.name](...) end, + __tostring = function(self) return (doclist[self.address] ~= nil and doclist[self.address][self.name] ~= nil) and doclist[self.address][self.name] or "function" end +} + +local vcomponent = {} + +function vcomponent.register(address, ctype, proxy, doc) + checkArg(1,address,"string") + checkArg(2,ctype,"string") + checkArg(3,proxy,"table") + if proxylist[address] ~= nil then + return nil, "component already at address" + elseif component.type(address) ~= nil then + return nil, "cannot register over real component" + end + proxy.address = address + proxy.type = ctype + local proxyobj = {} + for k,v in pairs(proxy) do + if type(v) == "function" then + proxyobj[k] = setmetatable({name=k,address=address},componentCallback) + else + proxyobj[k] = v + end + end + proxylist[address] = proxy + proxyobjs[address] = proxyobj + typelist[address] = ctype + doclist[address] = doc + computer.pushSignal("component_added",address,ctype) + return true +end + +function vcomponent.unregister(address) + checkArg(1,address,"string") + if proxylist[address] == nil then + if component.type(address) ~= nil then + return nil, "cannot unregister real component" + else + return nil, "no component at address" + end + end + local thetype = typelist[address] + proxylist[address] = nil + proxyobjs[address] = nil + typelist[address] = nil + doclist[address] = nil + computer.pushSignal("component_removed",address,thetype) + return true +end + +function vcomponent.list() + local list = {} + for k,v in pairs(proxylist) do + list[#list + 1] = {k,typelist[k],v} + end + return list +end + +function vcomponent.resolve(address, componentType) + checkArg(1, address, "string") + checkArg(2, componentType, "string", "nil") + for k,v in pairs(typelist) do + if componentType == nil or v == componentType then + if k:sub(1, #address) == address then + return k + end + end + end + return nil, "no such component" +end + +local r = math.random +function vcomponent.uuid() + return string.format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + r(0,255),r(0,255),r(0,255),r(0,255), + r(0,255),r(0,255), + r(64,79),r(0,255), + r(128,191),r(0,255), + r(0,255),r(0,255),r(0,255),r(0,255),r(0,255),r(0,255)) +end + +return vcomponent