Grammar refactoring

* Grammar refactoring WIP

* ISerializer.grammar

* Fixing various bugs in the grammar

* Small touch that improoves performance

* Fix unknown values grammar

* Various fixes

* Serialization refactoring to drop suboject logic

* Details fixed

* Entity attributes initialization refactoring

* JSDoc error fixed

* Rename value key to default

* Remove useless default

* Revert string keys
This commit is contained in:
barsdeveloper
2023-04-01 19:55:16 +02:00
committed by GitHub
parent b235d63348
commit bb24486303
44 changed files with 6395 additions and 6248 deletions

View File

@@ -1,8 +1,5 @@
import Grammar from "./Grammar.js"
import Parsimmon from "parsimmon"
import SerializerFactory from "./SerializerFactory.js"
import Utility from "../Utility.js"
import IEntity from "../entity/IEntity.js"
/**
* @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor
@@ -13,8 +10,6 @@ import IEntity from "../entity/IEntity.js"
/** @template {AnyValue} T */
export default class ISerializer {
static grammar = Parsimmon.createLanguage(new Grammar())
/** @param {AnyValueConstructor} entityType */
constructor(
entityType,
@@ -22,7 +17,7 @@ export default class ISerializer {
attributeSeparator = ",",
trailingSeparator = false,
attributeValueConjunctionSign = "=",
attributeKeyPrinter = k => k.join(".")
attributeKeyPrinter = k => k
) {
this.entityType = entityType
this.attributePrefix = attributePrefix
@@ -40,9 +35,9 @@ export default class ISerializer {
return this.read(value)
}
/** @param {T} object */
serialize(object, insideString = false, entity = object) {
return this.write(entity, object, insideString)
/** @param {T} value */
serialize(value, insideString = false) {
return this.write(value, insideString)
}
/**
@@ -56,21 +51,46 @@ export default class ISerializer {
/**
* @protected
* @param {T} object
* @param {T} entity
* @param {Boolean} insideString
* @returns {String}
*/
write(entity, object, insideString) {
throw new Error("Not implemented")
write(entity, insideString) {
let result = ""
const attributes = /** @type {EntityConstructor} */(entity.constructor).attributes ?? {}
const keys = Utility.mergeArrays(
Object.keys(attributes),
Object.keys(entity)
)
for (const key of keys) {
const value = entity[key]
if (value !== undefined && this.showProperty(entity, key)) {
const isSerialized = Utility.isSerialized(entity, key)
result += (result.length ? this.attributeSeparator : "")
+ this.attributePrefix
+ Utility.decodeKeyName(this.attributeKeyPrinter(key))
+ this.attributeValueConjunctionSign
+ (
isSerialized
? `"${this.writeValue(entity, key, true)}"`
: this.writeValue(entity, key, insideString)
)
}
}
if (this.trailingSeparator && result.length) {
// append separator at the end if asked and there was printed content
result += this.attributeSeparator
}
return result
}
/**
* @protected
* @param {AnyValue} value
* @param {String[]} fullKey
* @param {String} key
* @param {Boolean} insideString
*/
writeValue(entity, value, fullKey, insideString) {
writeValue(entity, key, insideString) {
const value = entity[key]
const type = Utility.getType(value)
// @ts-expect-error
const serializer = SerializerFactory.getSerializer(type)
@@ -78,65 +98,20 @@ export default class ISerializer {
throw new Error(`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`)
}
return serializer.write(
value instanceof IEntity ? value : entity,
value,
entity[key],
insideString
)
}
/**
* @protected
* @param {String[]} key
* @param {Object} object
* @param {Boolean} insideString
* @returns {String}
*/
subWrite(entity, key, object, insideString) {
let result = ""
let fullKey = key.concat("")
const last = fullKey.length - 1
const attributes = /** @type {EntityConstructor} */(object.constructor).attributes
const keys = attributes
? Utility.mergeArrays(
Object.keys(attributes),
Object.keys(object)
)
: Object.keys(object)
for (const property of keys) {
fullKey[last] = property
const value = object[property]
if (value?.constructor === Object) {
// Recursive call when finding an object
result += (result.length ? this.attributeSeparator : "")
+ this.subWrite(entity, fullKey, value, insideString)
} else if (value !== undefined && this.showProperty(entity, object, fullKey, value)) {
const isSerialized = Utility.isSerialized(entity, fullKey)
result += (result.length ? this.attributeSeparator : "")
+ this.attributePrefix
+ this.attributeKeyPrinter(fullKey)
+ this.attributeValueConjunctionSign
+ (
isSerialized
? `"${this.writeValue(entity, value, fullKey, true)}"`
: this.writeValue(entity, value, fullKey, insideString)
)
}
}
if (this.trailingSeparator && result.length && fullKey.length === 1) {
// append separator at the end if asked and there was printed content
result += this.attributeSeparator
}
return result
}
showProperty(entity, object, attributeKey, attributeValue) {
showProperty(entity, key) {
const attributes = /** @type {EntityConstructor} */(this.entityType).attributes
const attribute = Utility.objectGet(attributes, attributeKey)
const attribute = attributes[key]
const value = entity[key]
if (attribute?.constructor === Object) {
if (attribute.ignored) {
return false
}
return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault
return !Utility.equals(attribute.value, value) || attribute.showDefault
}
return true
}