"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"); } /* FIXME: Pass both key and value to custom deserializer */ module.exports = function createDeserializer(topLevelType, topLevelData, typeMap, options = {}) { let seen = new Map(); let placeholders = createPlaceholderManager(); let customDeserializer = defaultValue(options.deserializer, (value) => value); function deserializeSequenceItems(itemRule, entries) { let seenPlaceholder = false; let items = entries.map((item) => { let deserializedValue = deserializeEntry(itemRule, item); if (isPlaceholder(deserializedValue)) { seenPlaceholder = true; } return deserializedValue; }); return { items: items, seenPlaceholder: seenPlaceholder }; } function deserializeSet(rule, entries) { let {items, seenPlaceholder} = deserializeSequenceItems(rule._itemType, entries); let set = new Set(items); if (seenPlaceholder) { placeholders.markSet(set); } return set; } function deserializeArray(rule, entries) { let {items, seenPlaceholder} = deserializeSequenceItems(rule._itemType, entries); if (seenPlaceholder) { placeholders.markArray(items); } return items; } 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 (serializedData == null) { return customDeserializer(serializedData); } else 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 if (rule._collectionType === "array") { if (serializedData._d_sT === "gA") { return deserializeArray(rule, serializedData.entries); } else { /* FIXME: Clearer error message */ throw new Error("Expected guarded array, 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; };