From 5f8c756dc73cb6c94925e51e49d01f8bfb607ff5 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Mon, 1 Apr 2024 23:48:57 +0200 Subject: [PATCH] Enforcing type safety at import. --- .../LogarithmPlotter/js/history/common.mjs | 4 +- .../qml/eu/ad5001/LogarithmPlotter/js/io.mjs | 3 +- .../LogarithmPlotter/js/lib/qmlpolyfills.mjs | 2 + .../LogarithmPlotter/js/objs/common.mjs | 62 ++-- .../LogarithmPlotter/js/objs/function.mjs | 10 +- .../LogarithmPlotter/js/objs/repartition.mjs | 18 +- .../LogarithmPlotter/js/objs/sequence.mjs | 1 - .../ad5001/LogarithmPlotter/js/objs/text.mjs | 2 +- .../LogarithmPlotter/js/objs/xcursor.mjs | 4 +- .../ad5001/LogarithmPlotter/js/parameters.mjs | 276 +++++++++++++++++- 10 files changed, 327 insertions(+), 55 deletions(-) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs index 66d35fd..229a294 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs @@ -36,7 +36,7 @@ class HistoryCommonAPI extends Module { redo() { this.history.redo() } clear() { this.history.clear() } addToHistory(action) { this.history.addToHistory(action) } - unserialize(data) { this.history.unserialize(data) } + unserialize(...data) { this.history.unserialize(...data) } serialize() { return this.history.serialize() } } @@ -124,7 +124,7 @@ export class Action { } /** - * Returns a string with the HTML-formated description of the action. + * Returns a string with the HTML-formatted description of the action. * * @returns {string} */ diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/io.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/io.mjs index fe87ad8..f9fde47 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/io.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/io.mjs @@ -129,7 +129,8 @@ class IOAPI extends Module { if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) { Modules.Objects.currentObjects[objType] = [] for(let objData of data['objects'][objType]) { - let obj = new Modules.Objects.types[objType](...objData) + /** @type {DrawableObject} */ + let obj = Modules.Objects.types[objType].import(...objData) Modules.Objects.currentObjects[objType].push(obj) Modules.Objects.currentObjectsByName[obj.name] = obj } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs index 5302aca..bf0661e 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs @@ -26,6 +26,8 @@ qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTra qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); } /** @type {function(string, string): string} */ QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); } +/** @type {function(string): string} */ +QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); } /** @type {function(string|boolean|int): string} */ String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs index 5fb31ba..c38b5c1 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs @@ -20,6 +20,7 @@ import { getRandomColor, textsub } from "../utils.mjs" import Objects from "../objects.mjs" import Latex from "../math/latex.mjs" import {Module} from "../modules.mjs" +import {ensureTypeSafety, serializesByPropertyType} from "../parameters.mjs" // This file contains the default data to be imported from all other objects @@ -34,7 +35,7 @@ class ObjectsCommonAPI extends Module { } /** - * Creates a new name for an object, based on the \c allowedLetters. + * Creates a new name for an object, based on the allowedLetters. * If variables with each of the allowedLetters is created, a subscript * number is added to the name. * @param {string} allowedLetters @@ -56,7 +57,7 @@ class ObjectsCommonAPI extends Module { } /** - * Registers the object \c obj in the object list. + * Registers the object obj in the object list. * @param {DrawableObject} obj - Object to be registered. */ registerObject(obj) { @@ -131,6 +132,22 @@ export class DrawableObject { * @return {boolean} */ static executable() {return false} + + /** + * Imports the object from its serialized form. + * @return {DrawableObject} + */ + static import(name, visible, color, labelContent, ...args) { + let importedArgs = [name.toString(), visible === true, color.toString(), labelContent] + console.log('---') + console.log(this, name, args) + for(let [name, propType] of Object.entries(this.properties())) + if(!name.startsWith('comment')) { + console.log(name, propType, importedArgs.length-4, args[importedArgs.length-4]) + importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length-4])) + } + return new this(...importedArgs) + } /** * Base constructor for the object. @@ -138,7 +155,7 @@ export class DrawableObject { * @param {boolean} visible - true if the object is visible, false otherwise. * @param {color|string} color - Color of the object (can be string or QColor) * @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label. - * @constructor() + * @constructor */ constructor(name, visible = true, color = null, labelContent = 'name + value') { if(color == null) color = getRandomColor() @@ -150,15 +167,18 @@ export class DrawableObject { this.requiredBy = [] this.requires = [] } - + /** - * Serilizes the object in an array that can be JSON serialized. + * Serializes the object in an array that can be JSON serialized. * These parameters will be re-entered in the constructor when restored. * @return {array} */ export() { - // Should return what will be inputed as arguments when a file is loaded (serializable form) - return [this.name, this.visible, this.color.toString(), this.labelContent] + let exportList = [this.name, this.visible, this.color.toString(), this.labelContent] + for(let [name, propType] of Object.entries(this.constructor.properties())) + if(!name.startsWith('comment')) + exportList.push(serializesByPropertyType(propType, this[name])) + return exportList } /** @@ -182,7 +202,7 @@ export class DrawableObject { } /** - * Readable string content of the label depending on the value of the \c latexContent. + * Readable string content of the label depending on the value of the latexContent. * @return {string} */ getLabel() { @@ -198,7 +218,7 @@ export class DrawableObject { } /** - * Latex markup string content of the label depending on the value of the \c latexContent. + * Latex markup string content of the label depending on the value of the latexContent. * Every non latin character should be passed as latex symbols and formulas * should be in latex form. * See ../latex.mjs for helper methods. @@ -275,16 +295,15 @@ export class DrawableObject { } /** - * Abstract method. Draw the object onto the \c canvas with the. + * Abstract method. Draw the object onto the canvas with the. * @param {CanvasAPI} canvas - * @param {CanvasRenderingContext2D} ctx */ draw(canvas) {} /** - * Applicates a \c drawFunction with two position arguments depending on - * both the \c posX and \c posY of where the label should be displayed, - * and the \c labelPosition which declares the label should be displayed + * Applicates a drawFunction with two position arguments depending on + * both the posX and posY of where the label should be displayed, + * and the labelPosition which declares the label should be displayed * relatively to that position. * * @param {string|Enum} labelPosition - Position of the label relative to the marked position @@ -333,17 +352,16 @@ export class DrawableObject { } /** - * Automatically draw text (by default the label of the object on the \c canvas with - * the 2D context \c ctx depending on user settings. - * This method takes into account both the \c posX and \c posY of where the label - * should be displayed, including the \c labelPosition relative to it. - * The text is get both through the \c getLatexFunction and \c getTextFunction + * Automatically draw text (by default the label of the object on the canvas + * depending on user settings. + * This method takes into account both the posX and posY of where the label + * should be displayed, including the labelPosition relative to it. + * The text is get both through the getLatexFunction and getTextFunction * depending on whether to use latex. - * Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and - * \c drawFunctionText (x,y,text) depending on whether to use latex. + * Then, it's displayed using the drawFunctionLatex (x,y,imageData) and + * drawFunctionText (x,y,text) depending on whether to use latex. * * @param {CanvasAPI} canvas - * @param {CanvasRenderingContext2D} ctx * @param {string|Enum} labelPosition - Position of the label relative to the marked position * @param {number} posX - Component of the marked position on the x-axis * @param {number} posY - Component of the marked position on the y-axis diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs index 0d68830..2386a96 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs @@ -35,8 +35,8 @@ export default class Function extends ExecutableObject { 'comment', 'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}' ), - [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType, + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', 'comment2': QT_TRANSLATE_NOOP( 'comment', @@ -81,13 +81,13 @@ export default class Function extends ExecutableObject { return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}` } } - + export() { - return [this.name, this.visible, this.color.toString(), this.labelContent, - this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(), + return [this.name, this.visible, this.color.toString(), this.labelContent, + this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(), this.displayMode, this.labelPosition, this.labelX, this.drawPoints, this.drawDashedLines] } - + execute(x = 1) { if(this.definitionDomain.includes(x)) return this.expression.execute(x) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs index 0bee9a0..6c5245d 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs @@ -26,21 +26,25 @@ export default class RepartitionFunction extends ExecutableObject { static displayType(){return qsTr('Repartition')} static displayTypeMultiple(){return qsTr('Repartition functions')} static properties() {return { - [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, - [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', 'comment1': QT_TRANSLATE_NOOP( 'comment', 'Note: Specify the probability for each value.' ), - [QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '), + [QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '), + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', }} + static import(name, visible, color, labelContent, ...args) { + if(args.length === 5) + // Two legacy values no longer included. + args.shift(); args.shift() + return super.import(name, visible, color, labelContent, ...args) + } constructor(name = null, visible = true, color = null, labelContent = 'name + value', - beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) { + probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) { if(name == null) name = Common.getNewName('XYZUVW', "F_") super(name, visible, color, labelContent) - this.beginIncluded = beginIncluded - this.drawLineEnds = drawLineEnds this.probabilities = probabilities this.labelPosition = labelPosition this.labelX = labelX @@ -49,7 +53,7 @@ export default class RepartitionFunction extends ExecutableObject { export() { return [this.name, this.visible, this.color.toString(), this.labelContent, - this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX] + this.probabilities, this.labelPosition, this.labelX] } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs index a7af26f..a2e8673 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs @@ -64,7 +64,6 @@ export default class Sequence extends ExecutableObject { update() { console.log('Updating sequence', this.sequence) - console.trace() super.update() if( this.sequence == null || this.baseValues !== this.sequence.baseValues || diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs index e3e2ba2..ed62b81 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs @@ -32,7 +32,7 @@ export default class Text extends DrawableObject { [QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(), [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning, [QT_TRANSLATE_NOOP('prop','text')]: 'string', - 'comment1': QT_TRANSLATE_NOOP( + 'comment1': QT_TRANSLATE_NOOP( 'comment', 'If you have latex enabled, you can use use latex markup in between $$ to create equations.' ), diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs index 236bd4a..45727dd 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs @@ -29,8 +29,8 @@ export default class XCursor extends DrawableObject { static displayType(){return qsTr('X Cursor')} static displayTypeMultiple(){return qsTr('X Cursors')} static properties() {return { - [QT_TRANSLATE_NOOP('prop','x')]: 'Expression', - [QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'), + [QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(), + [QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject', true), [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean', [QT_TRANSLATE_NOOP('prop','rounding')]: 'number', diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.mjs index 2cf28a3..0427531 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.mjs @@ -1,72 +1,204 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * Copyright (C) 2021-2024 Ad5001 - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +import {parseDomain, Expression as Expr, Domain} from "./mathlib.mjs" -export class Expression { +const NONE = class Empty {} + +let stringValuesValidators = { + 'int': [parseInt, (x) => !isNaN(x)], + 'double': [parseFloat, (x) => !isNaN(x)], + 'string': [(x) => x, () => true] +} +let stringValidatorTypes = Object.keys(stringValuesValidators) + + +class PropertyType { + /** + * Validates if a value corresponds to the current property type, and if so, returns it. + * @param value + * @returns {null|object} + */ + parse(value) { + throw new TypeError(`validate function of ${typeof this} has not been implemented.`) + } + + /** + * Exports the value of this property type. + * @param value + * @returns {string|number|bool|object} + */ + export(value) { + throw new TypeError(`export function of ${typeof this} has not been implemented.`) + } +} + + +export class Expression extends PropertyType { constructor(...variables) { + super() this.type = 'Expression' this.variables = variables } toString() { - return this.variables.length == 0 ? 'Number' : `Expression(${this.variables.join(', ')})` + return this.variables.length === 0 ? 'Number' : `Expression(${this.variables.join(', ')})` + } + + parse(value) { + let result = NONE + if(typeof value == 'string') + try { + result = new Expr(value) + } catch(e) { + // Silently error and return null + console.trace() + console.log(`Error parsing expression ${value}:`) + console.error(e) + } + return result + } + + export(value) { + if(value instanceof Expr) + return value.toEditableString() + else + throw new TypeError(`Exportation error: ${value} is not an expression.`) } } -export class Enum { + +export class Enum extends PropertyType { constructor(...values) { + super() this.type = 'Enum' this.values = values + this.legacyValues = {} this.translatedValues = values.map(x => qsTr(x)) } toString() { - return this.type + return `${this.type}(${this.values.join(', ')})` + } + + parse(value) { + let result = NONE + if(this.values.includes(value)) + result = value + else if(this.legacyValues[value]) + result = this.legacyValues[value] + return result + } + + export(value) { + if(this.values.includes(value)) + return value + else if(this.legacyValues[value]) + return this.legacyValues[value] + else + throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(', ')}.`) } } -export class ObjectType { - constructor(objType) { + +export class ObjectType extends PropertyType { + constructor(objType, allowNull=false) { + super() this.type = 'ObjectType' this.objType = objType + this.allowNull = allowNull } toString() { return this.objType } + + parse(name) { + let result = NONE + if(typeof name == 'string' && name in Modules.Objects.currentObjectsByName) { + let obj = Modules.Objects.currentObjectsByName[name] + if (obj.type === this.objType || (this.objType === 'ExecutableObject' && obj.execute)) { + result = obj + } else { + // Silently error and return null + console.trace() + console.error(new TypeError(`Object ${name} is of not of type ${this.objType}:`)) + } + } else if(this.allowNull && (name == null || name === "null")) + result = null + return result + } + + export(value) { + if(value.type === this.objType || (this.objType === 'ExecutableObject' && value.execute)) + return value + else + throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`) + } } -export class List { + +export class List extends PropertyType { constructor(type, format = /^.+$/, label = '', forbidAdding = false) { + super() // type can be string, int and double. this.type = 'List' this.valueType = type + if(!stringValidatorTypes.includes(this.valueType)) + throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(', ')}.`) this.format = format this.label = label this.forbidAdding = forbidAdding } toString() { - return this.objType + return `${this.type}(${this.valueType}:${this.format})` + } + + parse(value) { + let result = NONE + if(typeof value == 'object' && value.__proto__ === Array) { + let list = [] + for(let v of value) { + if (this.format.test(v)) { + v = stringValuesValidators[this.valueType][0](v) + if(stringValuesValidators[this.valueType][1](v)) + list.append(v) + } + } + if(list.length === value.length) + result = value + } + return result + } + + export(value) { + if(typeof value == 'object' && value.__proto__ === Array) + return value + else + throw new TypeError(`Exportation error: ${value} not a list.`) + } } -export class Dictionary { + +export class Dictionary extends PropertyType { constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) { + super() // type & keyType can be string, int and double. this.type = 'Dict' this.valueType = type @@ -77,9 +209,35 @@ export class Dictionary { this.postKeyLabel = postKeyLabel this.forbidAdding = forbidAdding } - + toString() { - return 'Dictionary' + return `${this.type}(${this.keyType}:${this.keyFormat}: ${this.valueType}:${this.format})` + } + + parse(value) { + let result = NONE + if(typeof value == 'object' && value.__proto__ !== Array) { + let dict = [] + for(let [k, v] of Object.entries(value)) { + if (this.format.test(v) && this.keyFormat.test(k)) { + k = stringValuesValidators[this.keyType][0](k) + v = stringValuesValidators[this.valueType][0](v) + if(stringValuesValidators[this.keyType][1](k)) + if(stringValuesValidators[this.valueType][1](v)) + dict[k] = v + } + } + if(Object.keys(dict).length === Object.keys(value).length) + result = value + } + return result + } + + export(value) { + if(typeof value == 'object' && value.__proto__ !== Array) + return value + else + throw new TypeError(`Exportation error: ${value} not a dictionary.`) } } @@ -95,6 +253,14 @@ Enum.Position = new Enum( QT_TR_NOOP('below-left'), QT_TR_NOOP('below-right') ) +Enum.Position.legacyValues = { + 'top': 'above', + 'bottom': 'below', + 'top-left': 'above-left', + 'top-right': 'above-right', + 'bottom-left': 'below-left', + 'bottom-right': 'below-right', +} Enum.Positioning = new Enum( QT_TR_NOOP('center'), @@ -125,4 +291,86 @@ Enum.XCursorValuePosition = new Enum( QT_TR_NOOP('Hidden') ) +/** + * Ensures whether a provided value is of the corresponding type. + * @param {string|PropertyType} propertyType + * @param {string|number|boolean|array|object}value + * @returns {Object} + */ +export function ensureTypeSafety(propertyType, value) { + let result + let error = false + if(typeof propertyType == 'string') + switch(propertyType) { + case 'string': + result = value + error = typeof value !== 'string' + break + case 'number': + result = parseFloat(value) + error = isNaN(result) + break + case 'boolean': + result = value + error = value !== true && value !== false + break + case 'Domain': + try { + error = typeof value !== 'string' + if(!error) + result = parseDomain(value) + } catch(e) { + // Parse domain sometimes returns an empty set when it cannot parse a domain. + // It's okay though, it shouldn't be user parsed value, so returning an empty set + // is okay for a corrupted file, rather than erroring. + console.trace() + console.log(`Error parsing domain ${value}:`) + console.error(e) + error = true + } + break + } + else if(propertyType instanceof PropertyType) { + result = propertyType.parse(value) + error = result === NONE + } else + throw new TypeError(`Importation error: Unknown property type ${propertyType}.`) + if(error) { + console.trace() + throw new TypeError(`Importation error: Couldn't parse ${JSON.stringify(value)} as ${propertyType}.`) + } + return result +} +/** + * Serializes a property by its type to export into JSON. + * @param {string|PropertyType} propertyType + * @param value + * @returns {Object} + */ +export function serializesByPropertyType(propertyType, value) { + let result + if(typeof propertyType == 'string') + switch(propertyType) { + case 'string': + result = value.toString() + break + case 'number': + result = parseFloat(value) + break + case 'boolean': + result = value === true + break + case 'Domain': + if(value instanceof Domain) + result = value.toString() + else + throw new TypeError(`Exportation error: ${value} is not a domain.`) + break + } + else if(propertyType instanceof PropertyType) { + result = propertyType.export(value) + } else + throw new TypeError(`Exportation error: Unknown property type ${propertyType}.`) + return result +} \ No newline at end of file