"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ObjectCursor = void 0; const optional_1 = require("../../types/optional"); const clone_1 = require("../../util/clone"); const key_path_1 = require("./key-path"); const traverse_1 = require("./traverse"); class ObjectCursor { /** * Create a cursor for an object. * * @param root - An object to traverse. */ constructor(root) { this.values = [root]; this.keyPaths = [key_path_1.thisKeyPath]; this.savedDepths = []; } /** * The current value this cursor is pointing at. */ get currentValue() { return this.values[this.values.length - 1]; } /** * The key path of the value this cursor is pointing at. */ get currentKeyPath() { return this.keyPaths[this.keyPaths.length - 1]; } /** * Advance this cursor to a given value and the key path which * was used to reach it. * * Use this method to override the internal traversal logic of * the cursor as needed. Like `moveTo`, calls to this method can * be balanced with calls to `back`. * * @param value - The new value for the cursor to represent. * @param keyPath - The key path used to reach the value. */ interject(value, keyPath) { this.values.push(value); this.keyPaths.push(keyPath); } /** * Reconfigure this cursor to traverse a given object. * * @param newRoot - The new root object to traverse. * @param keyPath - The key path specifying where the root object came from. * Typically this should be `thisKeyPath` (the default value for this parameter.) */ reuse(newRoot, keyPath = key_path_1.thisKeyPath) { this.values.length = 0; this.values.push(newRoot); this.keyPaths.length = 0; this.keyPaths.push(keyPath); this.savedDepths.length = 0; } /** * Advance this cursor to a new position in the object it is traversing, * saving its previous position so that the cursor may be moved back. * * @param keyPath - A key path referring to a location in the cursor's current value. * @returns The new current value of the cursor. */ moveTo(keyPath) { const newValue = (0, traverse_1.traverse)(this.currentValue, keyPath); this.values.push(newValue); this.keyPaths.push(keyPath); return newValue; } /** * Rewind this cursor to its previous position in the object it is traversing. */ moveBack() { const currentDepth = this.values.length; if (currentDepth === 1) { throw new Error("Cannot move back past the root of a cursor"); } const numberOfSaves = this.savedDepths.length; if (numberOfSaves > 0 && currentDepth <= this.savedDepths[numberOfSaves - 1]) { throw new Error("Cannot move back past the most recent saved state"); } this.values.pop(); this.keyPaths.pop(); } /** * Save the current position of this cursor so that it may be restored later. * * Calls to this method must be balanced with a call to `restoreState`. */ saveState() { this.savedDepths.push(this.values.length); } /** * Restore this cursor's position to a previously saved state. * * Use this method to balance a previous call to `saveState`. */ restoreState() { const savedLength = this.savedDepths.pop(); if ((0, optional_1.isNothing)(savedLength)) { throw new Error("Calls to restoreState must balance previous calls to saveState"); } this.values.length = savedLength; this.keyPaths.length = savedLength; } // section Cloneable clone() { const copy = (0, clone_1.shallowCloneOf)(this); copy.values = this.values.slice(); copy.keyPaths = this.keyPaths.slice(); copy.savedDepths = this.savedDepths.slice(); return copy; } } exports.ObjectCursor = ObjectCursor; //# sourceMappingURL=object-cursor.js.map