summaryrefslogtreecommitdiff
path: root/script/json-beautify.lua
blob: f3467f0786601b8ad172bca9f6f32182e237aa54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
local json = require "json"
local type = type
local next = next
local error = error
local table_concat = table.concat
local table_sort = table.sort
local string_rep = string.rep
local setmetatable = setmetatable

local math_type

if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then
    local math_floor = math.floor
    function math_type(v)
        if v >= -2147483648 and v <= 2147483647 and math_floor(v) == v then
            return "integer"
        end
        return "float"
    end
else
    math_type = math.type
end

local statusVisited
local statusBuilder
local statusDep
local statusOpt

local defaultOpt = {
    newline = "\n",
    indent = "    ",
    depth = 0,
}
defaultOpt.__index = defaultOpt

local function encode_newline()
    statusBuilder[#statusBuilder+1] = statusOpt.newline..string_rep(statusOpt.indent, statusDep)
end

local encode_map = {}
local encode_string = json._encode_string
for k ,v in next, json._encode_map do
    encode_map[k] = v
end

local function encode(v)
    local res = encode_map[type(v)](v)
    statusBuilder[#statusBuilder+1] = res
end

function encode_map.string(v)
    statusBuilder[#statusBuilder+1] = '"'
    statusBuilder[#statusBuilder+1] = encode_string(v)
    return '"'
end

function encode_map.table(t)
    local first_val = next(t)
    if first_val == nil then
        if json.isObject(t) then
            return "{}"
        else
            return "[]"
        end
    end
    if statusVisited[t] then
        error("circular reference")
    end
    statusVisited[t] = true
    if type(first_val) == 'string' then
        local key = {}
        for k in next, t do
            if type(k) ~= "string" then
                error("invalid table: mixed or invalid key types: "..k)
            end
            key[#key+1] = k
        end
        table_sort(key)
        statusBuilder[#statusBuilder+1] = "{"
        statusDep = statusDep + 1
        encode_newline()
        do
            local k = key[1]
            statusBuilder[#statusBuilder+1] = '"'
            statusBuilder[#statusBuilder+1] = encode_string(k)
            statusBuilder[#statusBuilder+1] = '": '
            encode(t[k])
        end
        for i = 2, #key do
            local k = key[i]
            statusBuilder[#statusBuilder+1] = ","
            encode_newline()
            statusBuilder[#statusBuilder+1] = '"'
            statusBuilder[#statusBuilder+1] = encode_string(k)
            statusBuilder[#statusBuilder+1] = '": '
            encode(t[k])
        end
        statusDep = statusDep - 1
        encode_newline()
        statusVisited[t] = nil
        return "}"
    elseif json.supportSparseArray then
        local max = 0
        for k in next, t do
            if math_type(k) ~= "integer" or k <= 0 then
                error("invalid table: mixed or invalid key types: "..k)
            end
            if max < k then
                max = k
            end
        end
        statusBuilder[#statusBuilder+1] = "["
        statusDep = statusDep + 1
        encode_newline()
        encode(t[1])
        for i = 2, max do
            statusBuilder[#statusBuilder+1] = ","
            encode_newline()
            encode(t[i])
        end
        statusDep = statusDep - 1
        encode_newline()
        statusVisited[t] = nil
        return "]"
    else
        if t[1] == nil then
            error("invalid table: sparse array is not supported")
        end
        statusBuilder[#statusBuilder+1] = "["
        statusDep = statusDep + 1
        encode_newline()
        encode(t[1])
        local count = 2
        while t[count] ~= nil do
            statusBuilder[#statusBuilder+1] = ","
            encode_newline()
            encode(t[count])
            count = count + 1
        end
        if next(t, count-1) ~= nil then
            local k = next(t, count-1)
            if type(k) == "number" then
                error("invalid table: sparse array is not supported")
            else
                error("invalid table: mixed or invalid key types: "..k)
            end
        end
        statusDep = statusDep - 1
        encode_newline()
        statusVisited[t] = nil
        return "]"
    end
end

local function beautify_option(option)
    return setmetatable(option or {}, defaultOpt)
end

local function beautify_builder(builder, v, option)
    statusVisited = {}
    statusBuilder = builder
    statusOpt = beautify_option(option)
    statusDep = statusOpt.depth
    encode(v)
end

local function beautify(v, option)
    beautify_builder({}, v, option)
    return table_concat(statusBuilder)
end

json.beautify = beautify
json._beautify_builder = beautify_builder
json._beautify_option = beautify_option

return json