--[=[ @c Cache x Iterable @mt mem @d Iterable class that holds references to Discordia Class objects in no particular order. ]=] local json = require('json') local Iterable = require('iterables/Iterable') local null = json.null local Cache = require('class')('Cache', Iterable) local meta = {__mode = 'v'} function Cache:__init(array, constructor, parent) local objects = {} for _, data in ipairs(array) do local obj = constructor(data, parent) objects[obj:__hash()] = obj end self._count = #array self._objects = objects self._constructor = constructor self._parent = parent self._deleted = setmetatable({}, meta) end function Cache:__pairs() return next, self._objects end function Cache:__len() return self._count end local function insert(self, k, obj) self._objects[k] = obj self._count = self._count + 1 return obj end local function remove(self, k, obj) self._objects[k] = nil self._deleted[k] = obj self._count = self._count - 1 return obj end local function hash(data) -- local meta = getmetatable(data) -- debug -- assert(meta and meta.__jsontype == 'object') -- debug if data.id then -- snowflakes return data.id elseif data.user then -- members return data.user.id elseif data.emoji then -- reactions return data.emoji.id ~= null and data.emoji.id or data.emoji.name elseif data.code then -- invites return data.code else return nil, 'json data could not be hashed' end end function Cache:_insert(data) local k = assert(hash(data)) local old = self._objects[k] if old then old:_load(data) return old elseif self._deleted[k] then return insert(self, k, self._deleted[k]) else local obj = self._constructor(data, self._parent) return insert(self, k, obj) end end function Cache:_remove(data) local k = assert(hash(data)) local old = self._objects[k] if old then old:_load(data) return remove(self, k, old) elseif self._deleted[k] then return self._deleted[k] else return self._constructor(data, self._parent) end end function Cache:_delete(k) local old = self._objects[k] if old then return remove(self, k, old) elseif self._deleted[k] then return self._deleted[k] else return nil end end function Cache:_load(array, update) if update then local updated = {} for _, data in ipairs(array) do local obj = self:_insert(data) updated[obj:__hash()] = true end for obj in self:iter() do local k = obj:__hash() if not updated[k] then self:_delete(k) end end else for _, data in ipairs(array) do self:_insert(data) end end end --[=[ @m get @p k * @r * @d Returns an individual object by key, where the key should match the result of calling `__hash` on the contained objects. Unlike Iterable:get, this method operates with O(1) complexity. ]=] function Cache:get(k) return self._objects[k] end --[=[ @m iter @r function @d Returns an iterator that returns all contained objects. The order of the objects is not guaranteed. ]=] function Cache:iter() local objects, k, obj = self._objects return function() k, obj = next(objects, k) return obj end end return Cache