nuvira
Version:
Nuvira Database. New Database format (Readable & Easy to use), (Inbuilt Schema & constraints & rules & relations).
192 lines • 7.69 kB
JavaScript
export class NuviraSchema {
lines;
position;
parsedSchema;
errors;
allowedTypes;
schemaName;
constructor({ lines, position = 0, allowedTypes = [] }) {
this.lines = lines;
this.position = position;
this.parsedSchema = {};
this.errors = [];
this.allowedTypes = allowedTypes;
this.schemaName = 'unnamed-schema';
}
parseSchema() {
this.processSchemaName(this.lines);
this.schemaName = this.processSchemaName(this.lines) ?? "unnamed-schema";
while (this.position < this.lines.length) {
const line = this.lines[this.position].trim();
if (line.startsWith('!#')) {
this.position++;
continue;
}
if (line === "@end") {
break;
}
this.processLine(line);
this.position++;
}
return { schemaName: this.schemaName, parsedSchema: this.parsedSchema, errors: this.errors, lines: this.lines, position: this.position };
}
processSchemaName(lines) {
const schemaLine = lines.find(line => line.startsWith("@schema"));
if (!schemaLine) {
this.errors.push({
line: this.position + 1,
message: `Missing '@schema' declaration.`,
});
return null;
}
const match = schemaLine.match(/^:\s*([a-zA-Z0-9_-]+)$/);
if (match) {
this.schemaName = match[1];
}
return this.schemaName;
}
/**
* Processes each line of the schema and updates the parsedSchema and errors accordingly.
*
* @param {string} line - The line of schema to process.
*/
processLine(line) {
if (!line || line.startsWith('!#')) {
return;
}
const arrowIndex = line.indexOf("->");
if (arrowIndex !== -1) {
const key = line.substring(0, arrowIndex).trim();
const value = line.substring(arrowIndex + 2).trim();
const types = this.parseSubTypes(value);
if ((types.includes("Object") || types.includes("ObjectArray") || types.includes("Object[]")) &&
types.some((type) => type !== "Object" && type !== "ObjectArray" && type !== "Object[]")) {
this.errors.push({
line: this.position + 1,
message: `Invalid combination: ${types.join(", ")} for key: ${key}. Only 'Object' or 'ObjectArray' can be used with each other, but no other types.`,
});
return;
}
if (value.startsWith("Object {")) {
this.parsedSchema[key] = {
type: ["Object"],
properties: this.parseInlineNestedObject(),
};
}
else if (value.startsWith("ObjectArray {") || value.startsWith("Object[] {")) {
this.parsedSchema[key] = {
type: ["ObjectArray"],
items: this.parseInlineNestedArray(),
};
}
else {
const invalidTypes = types.filter((type) => !this.allowedTypes.includes(type));
if (invalidTypes.length > 0) {
this.errors.push({
line: this.position + 1,
message: `Invalid types: ${invalidTypes.join(", ")} for key: ${key}`,
});
return;
}
this.parsedSchema[key] = { type: types };
}
}
}
/**
* Parses a nested object schema defined inline within the schema.
*
* @returns {Record<string, any>} The parsed nested object schema.
*/
parseInlineNestedObject() {
const nestedObject = {};
this.position++;
while (this.position < this.lines.length) {
const line = this.lines[this.position].trim();
if (line.startsWith('!#')) {
this.position++;
continue;
}
if (line === "}")
break;
if (line.includes("->")) {
const arrowIndex = line.indexOf("->");
const nestedKey = line.substring(0, arrowIndex).trim();
const nestedValue = line.substring(arrowIndex + 2).trim();
const types = this.parseSubTypes(nestedValue);
// Define object type mappings
const objectTypes = new Map([
["Object {", { key: "properties", method: this.parseInlineNestedObject.bind(this) }],
["ObjectArray {", { key: "items", method: this.parseInlineNestedArray.bind(this) }],
["Object[] {", { key: "items", method: this.parseInlineNestedArray.bind(this) }]
]);
const objectType = [...objectTypes.keys()].find(type => nestedValue.startsWith(type));
if (objectType) {
nestedObject[nestedKey] = {
type: [objectType.replace(" {", "")],
[objectTypes.get(objectType).key]: objectTypes.get(objectType).method(),
};
}
else {
nestedObject[nestedKey] = { type: types };
}
}
this.position++;
}
return nestedObject;
}
/**
* Parses a nested array schema defined inline within the schema.
*
* @returns {Record<string, any>} The parsed nested array schema.
*/
parseInlineNestedArray() {
const arraySchema = {};
this.position++;
while (this.position < this.lines.length) {
const line = this.lines[this.position].trim();
if (line.startsWith('!#')) {
this.position++;
continue;
}
if (line === "}")
break;
if (line.includes("->")) {
const arrowIndex = line.indexOf("->");
const nestedKey = line.substring(0, arrowIndex).trim();
const nestedValue = line.substring(arrowIndex + 2).trim();
const types = this.parseSubTypes(nestedValue);
// Define object type mappings
const objectTypes = new Map([
["Object {", { key: "properties", method: this.parseInlineNestedObject.bind(this) }],
["ObjectArray {", { key: "items", method: this.parseInlineNestedArray.bind(this) }],
["Object[] {", { key: "items", method: this.parseInlineNestedArray.bind(this) }]
]);
const objectType = [...objectTypes.keys()].find(type => nestedValue.startsWith(type));
if (objectType) {
arraySchema[nestedKey] = {
type: [objectType.replace(" {", "")],
[objectTypes.get(objectType).key]: objectTypes.get(objectType).method(),
};
}
else {
arraySchema[nestedKey] = { type: types };
}
}
this.position++;
}
return arraySchema;
}
/**
* Parses the types defined in the schema for a given value.
*
* @param {string} value - The value to extract types from.
* @returns {string[]} An array of types.
*/
parseSubTypes(value) {
return value
.split("|")
.map(v => v.trim().replace(/[\s\{\}]+$/, ""))
.filter(Boolean);
}
}
//# sourceMappingURL=parseSchema.js.map