Update talents + beasts
This commit is contained in:
161
tools/luajson/json/encode.lua
Normal file
161
tools/luajson/json/encode.lua
Normal file
@ -0,0 +1,161 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local assert, error = assert, error
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
|
||||
local ipairs, pairs = ipairs, pairs
|
||||
local require = require
|
||||
|
||||
local output = require("json.encode.output")
|
||||
|
||||
local util = require("json.util")
|
||||
local util_merge, isCall = util.merge, util.isCall
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
--[[
|
||||
List of encoding modules to load.
|
||||
Loaded in sequence such that earlier encoders get priority when
|
||||
duplicate type-handlers exist.
|
||||
]]
|
||||
local modulesToLoad = {
|
||||
"strings",
|
||||
"number",
|
||||
"calls",
|
||||
"others",
|
||||
"array",
|
||||
"object"
|
||||
}
|
||||
-- Modules that have been loaded
|
||||
local loadedModules = {}
|
||||
|
||||
local json_encode = {}
|
||||
|
||||
-- Configuration bases for client apps
|
||||
local modes_defined = { "default", "strict" }
|
||||
|
||||
json_encode.default = {}
|
||||
json_encode.strict = {
|
||||
initialObject = true -- Require an object at the root
|
||||
}
|
||||
|
||||
-- For each module, load it and its defaults
|
||||
for _,name in ipairs(modulesToLoad) do
|
||||
local mod = require("json.encode." .. name)
|
||||
if mod.mergeOptions then
|
||||
for _, mode in pairs(modes_defined) do
|
||||
mod.mergeOptions(json_encode[mode], mode)
|
||||
end
|
||||
end
|
||||
loadedModules[name] = mod
|
||||
end
|
||||
|
||||
-- NOTE: Nested not found, so assume unsupported until use case arises
|
||||
local function flattenOutput(out, value)
|
||||
assert(type(value) ~= 'table')
|
||||
out = out or {}
|
||||
out[#out + 1] = value
|
||||
return out
|
||||
end
|
||||
|
||||
-- Prepares the encoding map from the already provided modules and new config
|
||||
local function prepareEncodeMap(options)
|
||||
local map = {}
|
||||
for _, name in ipairs(modulesToLoad) do
|
||||
local encodermap = loadedModules[name].getEncoder(options[name])
|
||||
for valueType, encoderSet in pairs(encodermap) do
|
||||
map[valueType] = flattenOutput(map[valueType], encoderSet)
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
--[[
|
||||
Encode a value with a given encoding map and state
|
||||
]]
|
||||
local function encodeWithMap(value, map, state, isObjectKey)
|
||||
local t = type(value)
|
||||
local encoderList = assert(map[t], "Failed to encode value, unhandled type: " .. t)
|
||||
for _, encoder in ipairs(encoderList) do
|
||||
local ret = encoder(value, state, isObjectKey)
|
||||
if false ~= ret then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
error("Failed to encode value, encoders for " .. t .. " deny encoding")
|
||||
end
|
||||
|
||||
|
||||
local function getBaseEncoder(options)
|
||||
local encoderMap = prepareEncodeMap(options)
|
||||
if options.preProcess then
|
||||
local preProcess = options.preProcess
|
||||
return function(value, state, isObjectKey)
|
||||
local ret = preProcess(value, isObjectKey or false)
|
||||
if nil ~= ret then
|
||||
value = ret
|
||||
end
|
||||
return encodeWithMap(value, encoderMap, state)
|
||||
end
|
||||
end
|
||||
return function(value, state, isObjectKey)
|
||||
return encodeWithMap(value, encoderMap, state)
|
||||
end
|
||||
end
|
||||
--[[
|
||||
Retreive an initial encoder instance based on provided options
|
||||
the initial encoder is responsible for initializing state
|
||||
State has at least these values configured: encode, check_unique, already_encoded
|
||||
]]
|
||||
function json_encode.getEncoder(options)
|
||||
options = options and util_merge({}, json_encode.default, options) or json_encode.default
|
||||
local encode = getBaseEncoder(options)
|
||||
|
||||
local function initialEncode(value)
|
||||
if options.initialObject then
|
||||
local errorMessage = "Invalid arguments: expects a JSON Object or Array at the root"
|
||||
assert(type(value) == 'table' and not isCall(value, options), errorMessage)
|
||||
end
|
||||
|
||||
local alreadyEncoded = {}
|
||||
local function check_unique(value)
|
||||
assert(not alreadyEncoded[value], "Recursive encoding of value")
|
||||
alreadyEncoded[value] = true
|
||||
end
|
||||
|
||||
local outputEncoder = options.output and options.output() or output.getDefault()
|
||||
local state = {
|
||||
encode = encode,
|
||||
check_unique = check_unique,
|
||||
already_encoded = alreadyEncoded, -- To unmark encoding when moving up stack
|
||||
outputEncoder = outputEncoder
|
||||
}
|
||||
local ret = encode(value, state)
|
||||
if nil ~= ret then
|
||||
return outputEncoder.simple and outputEncoder.simple(ret) or ret
|
||||
end
|
||||
end
|
||||
return initialEncode
|
||||
end
|
||||
|
||||
-- CONSTRUCT STATE WITH FOLLOWING (at least)
|
||||
--[[
|
||||
encoder
|
||||
check_unique -- used by inner encoders to make sure value is unique
|
||||
already_encoded -- used to unmark a value as unique
|
||||
]]
|
||||
function json_encode.encode(data, options)
|
||||
return json_encode.getEncoder(options)(data)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
mt.__call = function(self, ...)
|
||||
return json_encode.encode(...)
|
||||
end
|
||||
|
||||
setmetatable(json_encode, mt)
|
||||
|
||||
return json_encode
|
Reference in New Issue
Block a user