const QUOTE = '"';
export const NEWLINE = "\r\n";
export class StringifyError extends Error {
    name = "StringifyError";
}
function getEscapedString(value, sep) {
    if (value === undefined || value === null)
        return "";
    let str = "";
    if (typeof value === "object")
        str = JSON.stringify(value);
    else
        str = String(value);
    if (str.includes(sep) || str.includes(NEWLINE) || str.includes(QUOTE)) {
        return `${QUOTE}${str.replaceAll(QUOTE, `${QUOTE}${QUOTE}`)}${QUOTE}`;
    }
    return str;
}
function normalizeColumn(column) {
    let fn, header, prop;
    if (typeof column === "object") {
        if (Array.isArray(column)) {
            header = String(column[column.length - 1]);
            prop = column;
        }
        else {
            ({ fn } = column);
            prop = Array.isArray(column.prop) ? column.prop : [column.prop];
            header = typeof column.header === "string"
                ? column.header
                : String(prop[prop.length - 1]);
        }
    }
    else {
        header = String(column);
        prop = [column];
    }
    return { fn, header, prop };
}
async function getValuesFromItem(item, normalizedColumns) {
    const values = [];
    for (const column of normalizedColumns) {
        let value = item;
        for (const prop of column.prop) {
            if (typeof value !== "object" || value === null)
                continue;
            if (Array.isArray(value)) {
                if (typeof prop === "number")
                    value = value[prop];
                else {
                    throw new StringifyError('Property accessor is not of type "number"');
                }
            }
            else
                value = value[prop];
        }
        if (typeof column.fn === "function")
            value = await column.fn(value);
        values.push(value);
    }
    return values;
}
export async function stringify(data, columns, options = {}) {
    const { headers, separator: sep } = {
        headers: true,
        separator: ",",
        ...options,
    };
    if (sep.includes(QUOTE) || sep.includes(NEWLINE)) {
        const message = [
            "Separator cannot include the following strings:",
            '  - U+0022: Quotation mark (")',
            "  - U+000D U+000A: Carriage Return + Line Feed (\\r\\n)",
        ].join("\n");
        throw new StringifyError(message);
    }
    const normalizedColumns = columns.map(normalizeColumn);
    let output = "";
    if (headers) {
        output += normalizedColumns
            .map((column) => getEscapedString(column.header, sep))
            .join(sep);
        output += NEWLINE;
    }
    for (const item of data) {
        const values = await getValuesFromItem(item, normalizedColumns);
        output += values
            .map((value) => getEscapedString(value, sep))
            .join(sep);
        output += NEWLINE;
    }
    return output;
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 10960 characters]