You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
6.0 KiB

-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local TSM = select(2, ...) ---@type TSM
local StateSchema = TSM.Init("Util.ReactiveClasses.StateSchema") ---@class Util.ReactiveClasses.StateSchema
local State = TSM.Include("Util.ReactiveClasses.State")
local ReactiveStateSchema = TSM.Include("LibTSMClass").DefineClass("ReactiveStateSchema") ---@class ReactiveStateSchema
local VALID_TYPES = {
number = true,
string = true,
boolean = true,
table = true,
}
-- ============================================================================
-- Module Functions
-- ============================================================================
---Creates a new state schema object.
---@return ReactiveStateSchema
function StateSchema.Create()
return ReactiveStateSchema()
end
-- ============================================================================
-- ReactiveStateSchema - Class Meta Methods
-- ============================================================================
function ReactiveStateSchema:__init()
self._committed = false
self._fieldType = {}
self._default = {}
self._isOptional = {}
end
-- ============================================================================
-- ReactiveStateSchema - Public Class Methods
-- ============================================================================
---Defines a string field as part of the schema.
---@param key string The key of the new field
---@param default string The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddStringField(key, default)
return self:_AddField(key, "string", default, false)
end
---Defines a string field as part of the schema which can be nil.
---@param key string The key of the new field
---@param default? string The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddOptionalStringField(key, default)
return self:_AddField(key, "string", default, true)
end
---Defines a number field as part of the schema.
---@param key string The key of the new field
---@param default number The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddNumberField(key, default)
return self:_AddField(key, "number", default, false)
end
---Defines a number field as part of the schema which can be nil.
---@param key string The key of the new field
---@param default? number The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddOptionalNumberField(key, default)
return self:_AddField(key, "number", default, true)
end
---Defines a boolean field as part of the schema.
---@param key string The key of the new field
---@param default boolean The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddBooleanField(key, default)
return self:_AddField(key, "boolean", default, false)
end
---Defines a boolean field as part of the schema which can be nil.
---@param key string The key of the new field
---@param default? boolean The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddOptionalBooleanField(key, default)
return self:_AddField(key, "boolean", default, true)
end
---Defines a table field as part of the schema which can be nil (and is by default).
---@param key string The key of the new field
---@return ReactiveStateSchema
function ReactiveStateSchema:AddOptionalTableField(key)
return self:_AddField(key, "table", nil, true)
end
---Updates the default value of an existing field.
---@param key string The key of the field
---@param default? boolean The default value of the field
---@return ReactiveStateSchema
function ReactiveStateSchema:UpdateFieldDefault(key, default)
assert(not self._committed)
self:_ValidateValueForKey(key, default)
self._default[key] = default
return self
end
---Commits the schema and prevents further changes.
---@return ReactiveStateSchema
function ReactiveStateSchema:Commit()
self._committed = true
return self
end
---Creates a state object based on the schema.
---@return ReactiveState
function ReactiveStateSchema:CreateState()
assert(self._committed)
return State.Create(self)
end
---Returns a new state schema which extends the existing one
---@return ReactiveStateSchema
function ReactiveStateSchema:Extend()
assert(self._committed)
local newSchema = ReactiveStateSchema()
for key, fieldType in self:_FieldIterator() do
newSchema:_AddField(key, fieldType, self._default[key], self._isOptional[key])
end
return newSchema
end
-- ============================================================================
-- ReactiveStateSchema - Private Class Methods
-- ============================================================================
function ReactiveStateSchema:_AddField(key, fieldType, default, isOptional)
assert(not self._committed)
assert(not self._fieldType[key])
self._fieldType[key] = fieldType
self._default[key] = default
self._isOptional[key] = isOptional
self:_ValidateValueForKey(key, default)
return self
end
function ReactiveStateSchema:_ApplyDefaults(data)
for key in self:_FieldIterator() do
data[key] = self._default[key]
end
end
function ReactiveStateSchema:_ValidateValueForKey(key, value)
local fieldType = self._fieldType[key]
if not fieldType then
error("Invalid state key: "..tostring(key), 2)
elseif not VALID_TYPES[fieldType] then
error("Unexpected fieldType: "..tostring(fieldType))
elseif type(value) ~= fieldType and (value ~= nil or not self._isOptional[key]) then
error("Invalid type for state key ("..tostring(key).."): "..tostring(value))
end
end
function ReactiveStateSchema:_HasKey(key)
return self._fieldType[key] ~= nil
end
function ReactiveStateSchema:_FieldIterator()
return pairs(self._fieldType)
end