import { luaRawLen , luaAssert } from "./luafuncs" ; export namespace Format { export const arrayTag = { } as "$arrayTag" ; export interface ExplicitArray { [ arrayTag ] ? : boolean ; } export function makeExplicitArray < T = unknown > ( arr = [ ] ) { ( arr as ExplicitArray ) [ arrayTag ] = true ; return arr ; } const indentStr = "  " ; const escapes = { "\n" : "\\n" , "\r" : "\\r" , "\"" : "\\\"" , "\\" : "\\\\" , "\b" : "\\b" , "\f" : "\\f" , "\t" : "\\t" , } ; const escapesPattern = "[\n\r\"\\\b\f\t%z\x01-\x1F]" ; function replaceEscape ( char ) { const [ byte ] = luaAssert ( ... string . byte ( char ) ) ; if ( byte >= 0 && byte < 32 ) { return string . format ( "\\u%.4X" , byte ) ; } return luaAssert ( escapes [ char ] ) ; } function escape ( str ) { const [ escaped ] = str . gsub ( escapesPattern , replaceEscape ) ; return escaped ; } function isArray ( val ) { if ( ( val as ExplicitArray ) [ arrayTag ] ) { return true ; } const len = luaRawLen ( val ) ; if ( len === 0 ) { return false ; } for ( const [ k ] of pairs ( val ) ) { if ( typeof k !== "number" || k > len ) { return false ; } } return true ; } export function asJson ( val , indent = 0 , tables ? ) { tables = tables ?? new LuaTable ( ) ; const valType = type ( val ) ; if ( valType === "table" && ! tables . get ( val ) ) { tables . set ( val , true ) ; if ( isArray ( val ) ) { const arrayVals = [ ] ; for ( const [ _ , arrayVal ] of ipairs ( val ) ) { const valStr = asJson ( arrayVal , indent + 1 , tables ) ; table . insert ( arrayVals , `\n${ indentStr . rep ( indent + 1 ) }${ valStr }` ) ; } return `[${ table . concat ( arrayVals , "," ) }\n${ indentStr . rep ( indent ) }]` ; } else { const kvps = [ ] ; for ( const [ k , v ] of pairs ( val as AnyTable ) ) { const valStr = asJson ( v , indent + 1 , tables ) ; table . insert ( kvps , `\n${ indentStr . rep ( indent + 1 ) }"${ escape ( tostring ( k ) ) }": ${ valStr }` ) ; } return ( kvps . length > 0 ) ? `{${ table . concat ( kvps , "," ) }\n${ indentStr . rep ( indent ) }}` : "{}" ; } } else if ( valType === "number" || valType === "boolean" ) { return tostring ( val ) ; } else { return `"${ escape ( tostring ( val ) ) }"` ; } } }