@gulujs/toml
Version:
TOML parser and serializer
151 lines (150 loc) • 6.19 kB
JavaScript
import { DUPLICATE_KEY_MESSAGE, DUPLICATE_TABLE_NAME_MESSAGE, ARRAY_OF_TABLES_NAME_IS_DECLARED_AS_KEY_MESSAGE, ARRAY_OF_TABLES_NAME_IS_DECLARED_AS_OTHER_TYPE_MESSAGE, TABLE_NAME_IS_ALREADY_DECLARED_AS_NON_TABLE_MESSAGE, TABLE_NAME_IS_DECLARED_AS_ARRAY_OF_TABLES_MESSAGE, KEY_IS_NOT_ALLOWED_TO_ADD_TO_TABLE_MESSAGE, FAILED_TO_ACCESS_AS_TABLE_MESSAGE, TableObjectError, CANNOT_EXTEND_TABLES_WITHIN_STATIC_ARRAYS_MESSAGE } from '../errors/index.js';
import { TableComment } from './table-comment.js';
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
export class TableObject {
constructor(options) {
this.root = {};
this.currentTable = this.root;
this.currentTablePath = null;
this.isCurrentTableArray = false;
this.enableTableComment = false;
this.tableSet = new Set();
this.arrayOfTablesSet = new Set();
this.objectSet = new Set();
this.comments = [];
this.enableTableComment = options?.enableTableComment === true;
}
set(path, value) {
this.clearComments();
const { node } = this.getNode(this.currentTable, path, 'set');
const key = path[path.length - 1];
if (hasOwn(node, key)) {
throw new TableObjectError(DUPLICATE_KEY_MESSAGE(path, this.currentTablePath, this.isCurrentTableArray));
}
node[key] = value;
}
switchTable(path) {
const { node, hasDefinedTable } = this.getNode(this.root, path, 'switchTable');
const key = path[path.length - 1];
if (!hasOwn(node, key)) {
const child = {};
node[key] = child;
this.objectSet.add(child);
this.tableSet.add(child);
this.currentTable = child;
this.currentTablePath = path;
this.isCurrentTableArray = false;
this.setCurrentTableComment();
return;
}
if (hasDefinedTable) {
throw new TableObjectError(DUPLICATE_TABLE_NAME_MESSAGE(path));
}
const child = node[key];
if (this.tableSet.has(child)) {
throw new TableObjectError(DUPLICATE_TABLE_NAME_MESSAGE(path));
}
if (this.objectSet.has(child)) {
this.tableSet.add(node);
this.currentTable = child;
this.currentTablePath = path;
this.isCurrentTableArray = false;
this.setCurrentTableComment();
}
else if (Array.isArray(child)) {
throw new TableObjectError(TABLE_NAME_IS_DECLARED_AS_ARRAY_OF_TABLES_MESSAGE(path));
}
else {
throw new TableObjectError('Unexpected');
}
}
switchArrayOfTables(path) {
const { node } = this.getNode(this.root, path, 'switchArrayOfTables');
const key = path[path.length - 1];
if (!hasOwn(node, key)) {
const child = [{}];
node[key] = child;
this.arrayOfTablesSet.add(child);
this.currentTable = child[0];
this.currentTablePath = path;
this.isCurrentTableArray = true;
this.setCurrentTableComment();
return;
}
const child = node[key];
if (!Array.isArray(child)) {
throw new TableObjectError(ARRAY_OF_TABLES_NAME_IS_DECLARED_AS_OTHER_TYPE_MESSAGE(path));
}
if (!this.arrayOfTablesSet.has(child)) {
throw new TableObjectError(ARRAY_OF_TABLES_NAME_IS_DECLARED_AS_KEY_MESSAGE(path));
}
this.currentTable = {};
this.currentTablePath = path;
this.isCurrentTableArray = true;
child.push(this.currentTable);
this.setCurrentTableComment();
}
getNode(node, path, action) {
let hasDefinedTable = false;
for (let i = 0, len = path.length - 1; i < len; i++) {
const key = path[i];
if (!hasOwn(node, key)) {
node[key] = {};
node = node[key];
this.objectSet.add(node);
continue;
}
const child = node[key];
if (action === 'set') {
if (this.tableSet.has(child)) {
throw new TableObjectError(KEY_IS_NOT_ALLOWED_TO_ADD_TO_TABLE_MESSAGE(path, this.currentTablePath, i, false));
}
if (this.objectSet.has(child)) {
node = child;
}
else if (Array.isArray(child) && this.arrayOfTablesSet.has(child)) {
throw new TableObjectError(KEY_IS_NOT_ALLOWED_TO_ADD_TO_TABLE_MESSAGE(path, this.currentTablePath, i, true));
}
else {
throw new TableObjectError(FAILED_TO_ACCESS_AS_TABLE_MESSAGE(path, i, this.currentTablePath, this.isCurrentTableArray));
}
continue;
}
if (action === 'switchTable') {
if (this.tableSet.has(child)) {
hasDefinedTable = true;
}
}
// 'switchTable' and 'switchArrayOfTables'
if (this.objectSet.has(child)) {
node = child;
}
else if (Array.isArray(child) && this.arrayOfTablesSet.has(child)) {
node = child[child.length - 1];
}
else {
if (Array.isArray(child)) {
throw new TableObjectError(CANNOT_EXTEND_TABLES_WITHIN_STATIC_ARRAYS_MESSAGE(path, i));
}
else {
throw new TableObjectError(TABLE_NAME_IS_ALREADY_DECLARED_AS_NON_TABLE_MESSAGE(path, i, action === 'switchArrayOfTables'));
}
}
}
return { node, hasDefinedTable };
}
addComment(comment) {
this.comments.push(comment);
}
clearComments() {
if (this.enableTableComment) {
this.comments = [];
}
}
setCurrentTableComment() {
if (!this.enableTableComment || !this.comments.length) {
return;
}
TableComment.setComment(this.currentTable, this.comments.join('\n'));
}
}