// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
/** {@linkcode parse} and {@linkcode stringify} for handling
 * [TOML](https://toml.io/en/) encoded data.
 *
 * This module is browser compatible.
 *
 * @module
 */ export { parse } from "./_toml/parser.ts";
// Bare keys may only contain ASCII letters,
// ASCII digits, underscores, and dashes (A-Za-z0-9_-).
function joinKeys(keys) {
    // Dotted keys are a sequence of bare or quoted keys joined with a dot.
    // This allows for grouping similar properties together:
    return keys.map((str)=>{
        return str.match(/[^A-Za-z0-9_-]/) ? JSON.stringify(str) : str;
    }).join(".");
}
var ArrayType;
(function(ArrayType) {
    ArrayType[ArrayType["ONLY_PRIMITIVE"] = 0] = "ONLY_PRIMITIVE";
    ArrayType[ArrayType["ONLY_OBJECT_EXCLUDING_ARRAY"] = 1] = "ONLY_OBJECT_EXCLUDING_ARRAY";
    ArrayType[ArrayType["MIXED"] = 2] = "MIXED";
})(ArrayType || (ArrayType = {}));
class Dumper {
    maxPad = 0;
    srcObject;
    output = [];
    #arrayTypeCache = new Map();
    constructor(srcObjc){
        this.srcObject = srcObjc;
    }
    dump(fmtOptions = {}) {
        // deno-lint-ignore no-explicit-any
        this.output = this.#printObject(this.srcObject);
        this.output = this.#format(fmtOptions);
        return this.output;
    }
    #printObject(obj, keys = []) {
        const out = [];
        const props = Object.keys(obj);
        const inlineProps = [];
        const multilineProps = [];
        for (const prop of props){
            if (this.#isSimplySerializable(obj[prop])) {
                inlineProps.push(prop);
            } else {
                multilineProps.push(prop);
            }
        }
        const sortedProps = inlineProps.concat(multilineProps);
        for(let i = 0; i < sortedProps.length; i++){
            const prop1 = sortedProps[i];
            const value = obj[prop1];
            if (value instanceof Date) {
                out.push(this.#dateDeclaration([
                    prop1
                ], value));
            } else if (typeof value === "string" || value instanceof RegExp) {
                out.push(this.#strDeclaration([
                    prop1
                ], value.toString()));
            } else if (typeof value === "number") {
                out.push(this.#numberDeclaration([
                    prop1
                ], value));
            } else if (typeof value === "boolean") {
                out.push(this.#boolDeclaration([
                    prop1
                ], value));
            } else if (value instanceof Array) {
                const arrayType = this.#getTypeOfArray(value);
                if (arrayType === ArrayType.ONLY_PRIMITIVE) {
                    out.push(this.#arrayDeclaration([
                        prop1
                    ], value));
                } else if (arrayType === ArrayType.ONLY_OBJECT_EXCLUDING_ARRAY) {
                    // array of objects
                    for(let i1 = 0; i1 < value.length; i1++){
                        out.push("");
                        out.push(this.#headerGroup([
                            ...keys,
                            prop1
                        ]));
                        out.push(...this.#printObject(value[i1], [
                            ...keys,
                            prop1
                        ]));
                    }
                } else {
                    // this is a complex array, use the inline format.
                    const str = value.map((x)=>this.#printAsInlineValue(x)).join(",");
                    out.push(`${this.#declaration([
                        prop1
                    ])}[${str}]`);
                }
            } else if (typeof value === "object") {
                out.push("");
                out.push(this.#header([
                    ...keys,
                    prop1
                ]));
                if (value) {
                    const toParse = value;
                    out.push(...this.#printObject(toParse, [
                        ...keys,
                        prop1
                    ]));
                }
            // out.push(...this._parse(value, `${path}${prop}.`));
            }
        }
        out.push("");
        return out;
    }
    #isPrimitive(value1) {
        return value1 instanceof Date || value1 instanceof RegExp || [
            "string",
            "number",
            "boolean"
        ].includes(typeof value1);
    }
    #getTypeOfArray(arr) {
        if (this.#arrayTypeCache.has(arr)) {
            return this.#arrayTypeCache.get(arr);
        }
        const type = this.#doGetTypeOfArray(arr);
        this.#arrayTypeCache.set(arr, type);
        return type;
    }
    #doGetTypeOfArray(arr1) {
        if (!arr1.length) {
            // any type should be fine
            return ArrayType.ONLY_PRIMITIVE;
        }
        const onlyPrimitive = this.#isPrimitive(arr1[0]);
        if (arr1[0] instanceof Array) {
            return ArrayType.MIXED;
        }
        for(let i2 = 1; i2 < arr1.length; i2++){
            if (onlyPrimitive !== this.#isPrimitive(arr1[i2]) || arr1[i2] instanceof Array) {
                return ArrayType.MIXED;
            }
        }
        return onlyPrimitive ? ArrayType.ONLY_PRIMITIVE : ArrayType.ONLY_OBJECT_EXCLUDING_ARRAY;
    }
    #printAsInlineValue(value2) {
        if (value2 instanceof Date) {
            return `"${this.#printDate(value2)}"`;
        } else if (typeof value2 === "string" || value2 instanceof RegExp) {
            return JSON.stringify(value2.toString());
        } else if (typeof value2 === "number") {
            return value2;
        } else if (typeof value2 === "boolean") {
            return value2.toString();
        } else if (value2 instanceof Array) {
            const str1 = value2.map((x)=>this.#printAsInlineValue(x)).join(",");
            return `[${str1}]`;
        } else if (typeof value2 === "object") {
            if (!value2) {
                throw new Error("should never reach");
            }
            const str2 = Object.keys(value2).map((key)=>{
                // deno-lint-ignore no-explicit-any
                return `${key} = ${this.#printAsInlineValue(value2[key])}`;
            }).join(",");
            return `{${str2}}`;
        }
        throw new Error("should never reach");
    }
    #isSimplySerializable(value3) {
        return typeof value3 === "string" || typeof value3 === "number" || typeof value3 === "boolean" || value3 instanceof RegExp || value3 instanceof Date || value3 instanceof Array && this.#getTypeOfArray(value3) !== ArrayType.ONLY_OBJECT_EXCLUDING_ARRAY;
    }
    #header(keys1) {
        return `[${joinKeys(keys1)}]`;
    }
    #headerGroup(keys2) {
        return `[[${joinKeys(keys2)}]]`;
    }
    #declaration(keys3) {
        const title = joinKeys(keys3);
        if (title.length > this.maxPad) {
            this.maxPad = title.length;
        }
        return `${title} = `;
    }
    #arrayDeclaration(keys4, value4) {
        return `${this.#declaration(keys4)}${JSON.stringify(value4)}`;
    }
    #strDeclaration(keys5, value5) {
        return `${this.#declaration(keys5)}${JSON.stringify(value5)}`;
    }
    #numberDeclaration(keys6, value6) {
        switch(value6){
            case Infinity:
                return `${this.#declaration(keys6)}inf`;
            case -Infinity:
                return `${this.#declaration(keys6)}-inf`;
            default:
                return `${this.#declaration(keys6)}${value6}`;
        }
    }
    #boolDeclaration(keys7, value7) {
        return `${this.#declaration(keys7)}${value7}`;
    }
    #printDate(value8) {
        function dtPad(v, lPad = 2) {
            return v.padStart(lPad, "0");
        }
        const m = dtPad((value8.getUTCMonth() + 1).toString());
        const d = dtPad(value8.getUTCDate().toString());
        const h = dtPad(value8.getUTCHours().toString());
        const min = dtPad(value8.getUTCMinutes().toString());
        const s = dtPad(value8.getUTCSeconds().toString());
        const ms = dtPad(value8.getUTCMilliseconds().toString(), 3);
        // formatted date
        const fData = `${value8.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`;
        return fData;
    }
    #dateDeclaration(keys8, value9) {
        return `${this.#declaration(keys8)}${this.#printDate(value9)}`;
    }
    #format(options = {}) {
        const { keyAlignment =false  } = options;
        const rDeclaration = /^(\".*\"|[^=]*)\s=/;
        const out1 = [];
        for(let i3 = 0; i3 < this.output.length; i3++){
            const l = this.output[i3];
            // we keep empty entry for array of objects
            if (l[0] === "[" && l[1] !== "[") {
                // empty object
                if (this.output[i3 + 1] === "") {
                    i3 += 1;
                    continue;
                }
                out1.push(l);
            } else {
                if (keyAlignment) {
                    const m1 = rDeclaration.exec(l);
                    if (m1) {
                        out1.push(l.replace(m1[1], m1[1].padEnd(this.maxPad)));
                    } else {
                        out1.push(l);
                    }
                } else {
                    out1.push(l);
                }
            }
        }
        // Cleaning multiple spaces
        const cleanedOutput = [];
        for(let i4 = 0; i4 < out1.length; i4++){
            const l1 = out1[i4];
            if (!(l1 === "" && out1[i4 + 1] === "")) {
                cleanedOutput.push(l1);
            }
        }
        return cleanedOutput;
    }
}
/**
 * Stringify dumps source object into TOML string and returns it.
 * @param srcObj
 * @param [fmtOptions] format options
 * @param [fmtOptions.keyAlignment] whether to algin key
 */ export function stringify(srcObj, fmtOptions) {
    return new Dumper(srcObj).dump(fmtOptions).join("\n");
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 21772 characters]