You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
4.3 KiB
JavaScript
152 lines
4.3 KiB
JavaScript
"use strict";
|
|
|
|
const defaultValue = require("default-value");
|
|
const mapObj = require("map-obj");
|
|
|
|
const isPlaceholder = require("../util/is-placeholder");
|
|
const createPlaceholderManager = require("./placeholder-manager");
|
|
|
|
function isReference(serializedData) {
|
|
return (serializedData != null && serializedData._d_sT === "cTR");
|
|
}
|
|
|
|
module.exports = function createDeserializer(topLevelType, topLevelData, typeMap, options = {}) {
|
|
let seen = new Map();
|
|
let placeholders = createPlaceholderManager();
|
|
|
|
let customDeserializer = defaultValue(options.deserializer, (value) => value);
|
|
|
|
function deserializeSet(rule, entries) {
|
|
let seenPlaceholder = false;
|
|
|
|
let set = new Set(entries.map((item) => {
|
|
let deserializedValue = deserializeEntry(rule._itemType, item);
|
|
|
|
if (isPlaceholder(deserializedValue)) {
|
|
seenPlaceholder = true;
|
|
}
|
|
|
|
return deserializedValue;
|
|
}));
|
|
|
|
if (seenPlaceholder) {
|
|
placeholders.markSet(set);
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
function deserializeMap(rule, entries) {
|
|
let seenPlaceholder = false;
|
|
|
|
let map = new Map(entries.map(([itemKey, itemValue]) => {
|
|
let deserializedKey = deserializeEntry(rule._keyType, itemKey);
|
|
let deserializedValue = deserializeEntry(rule._itemType, itemValue);
|
|
|
|
if (isPlaceholder(deserializedKey) || isPlaceholder(deserializedValue)) {
|
|
seenPlaceholder = true;
|
|
}
|
|
|
|
return [
|
|
deserializedKey,
|
|
deserializedValue
|
|
];
|
|
}));
|
|
|
|
if (seenPlaceholder) {
|
|
placeholders.markMap(map);
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
function deserializeEntry(rule, serializedData) {
|
|
if (rule._isCustomType) {
|
|
return deserializeInstanceOrReference(rule, serializedData);
|
|
} else if (rule._isCustomRegistryType) {
|
|
return deserializeInstanceOrReference(rule._registry._getType(rule._name), serializedData);
|
|
} else if (rule._isRegistryTrait || rule._isTrait) {
|
|
return deserializeInstanceFuzzy(serializedData);
|
|
} else if (rule._collectionType === "set") {
|
|
if (serializedData._d_sT === "gS") {
|
|
return deserializeSet(rule, serializedData.entries);
|
|
} else {
|
|
/* FIXME: Clearer error message */
|
|
throw new Error("Expected guarded set, but got something else instead");
|
|
}
|
|
} else if (rule._collectionType === "map") {
|
|
if (serializedData._d_sT === "gM") {
|
|
return deserializeMap(rule, serializedData.entries);
|
|
} else {
|
|
/* FIXME: Clearer error message */
|
|
throw new Error("Expected guarded map, but got something else instead");
|
|
}
|
|
} else {
|
|
/* This also covers `either` rules where the serializedData is a type instance or reference. */
|
|
return deserializeEntryFuzzy(serializedData);
|
|
}
|
|
}
|
|
|
|
function deserializeEntryFuzzy(serializedData) {
|
|
if (serializedData != null && (serializedData._d_sT === "cT" || serializedData._d_sT === "cTR")) {
|
|
return deserializeInstanceFuzzy(serializedData);
|
|
} else {
|
|
return customDeserializer(serializedData);
|
|
}
|
|
}
|
|
|
|
function deserializeInstance(typeFactory, serializedData) {
|
|
let placeholderProperties = [];
|
|
|
|
let deserializedData = mapObj(serializedData.data, (key, value) => {
|
|
let rule = typeFactory._cumulativeSchema[key];
|
|
let deserializedValue = deserializeEntry(rule, value);
|
|
|
|
if (isPlaceholder(deserializedValue)) {
|
|
placeholderProperties.push(key);
|
|
}
|
|
|
|
return [key, deserializedValue];
|
|
});
|
|
|
|
let instance = typeFactory(deserializedData);
|
|
|
|
for (let property of placeholderProperties) {
|
|
placeholders.markProperty(instance, property);
|
|
}
|
|
|
|
seen.set(serializedData.id, instance);
|
|
|
|
return instance;
|
|
}
|
|
|
|
function deserializeReference(serializedData) {
|
|
return {
|
|
_isDeserializationPlaceholder: true,
|
|
_deserializationId: serializedData.id
|
|
};
|
|
}
|
|
|
|
function deserializeInstanceOrReference(typeFactory, serializedData) {
|
|
if (isReference(serializedData)) {
|
|
return deserializeReference(serializedData);
|
|
} else {
|
|
return deserializeInstance(typeFactory, serializedData);
|
|
}
|
|
}
|
|
|
|
function deserializeInstanceFuzzy(serializedData) {
|
|
if (isReference(serializedData)) {
|
|
return deserializeReference(serializedData);
|
|
} else if (typeMap.has(serializedData.type)) {
|
|
return deserializeInstance(typeMap.get(serializedData.type), serializedData);
|
|
} else {
|
|
throw new Error(`Encountered instance of an unknown type with hash ${serializedData.type}`);
|
|
}
|
|
}
|
|
|
|
let topLevelInstance = deserializeInstance(topLevelType, topLevelData);
|
|
placeholders.fillInPlaceholders(seen);
|
|
return topLevelInstance;
|
|
};
|