|
|
'use strict';
const Clone = require('@hapi/hoek/lib/clone'); const Reach = require('@hapi/hoek/lib/reach');
const Common = require('./common');
const internals = { value: Symbol('value') };
module.exports = internals.State = class {
constructor(path, ancestors, state) {
this.path = path; this.ancestors = ancestors; // [parent, ..., root]
this.mainstay = state.mainstay; this.schemas = state.schemas; // [current, ..., root]
this.debug = null; }
localize(path, ancestors = null, schema = null) {
const state = new internals.State(path, ancestors, this);
if (schema && state.schemas) {
state.schemas = [internals.schemas(schema), ...state.schemas]; }
return state; }
nest(schema, debug) {
const state = new internals.State(this.path, this.ancestors, this); state.schemas = state.schemas && [internals.schemas(schema), ...state.schemas]; state.debug = debug; return state; }
shadow(value, reason) {
this.mainstay.shadow = this.mainstay.shadow || new internals.Shadow(); this.mainstay.shadow.set(this.path, value, reason); }
snapshot() {
if (this.mainstay.shadow) { this._snapshot = Clone(this.mainstay.shadow.node(this.path)); }
this.mainstay.snapshot(); }
restore() {
if (this.mainstay.shadow) { this.mainstay.shadow.override(this.path, this._snapshot); this._snapshot = undefined; }
this.mainstay.restore(); }
commit() {
if (this.mainstay.shadow) { this.mainstay.shadow.override(this.path, this._snapshot); this._snapshot = undefined; }
this.mainstay.commit(); } };
internals.schemas = function (schema) {
if (Common.isSchema(schema)) { return { schema }; }
return schema; };
internals.Shadow = class {
constructor() {
this._values = null; }
set(path, value, reason) {
if (!path.length) { // No need to store root value
return; }
if (reason === 'strip' && typeof path[path.length - 1] === 'number') { // Cannot store stripped array values (due to shift)
return; }
this._values = this._values || new Map();
let node = this._values; for (let i = 0; i < path.length; ++i) { const segment = path[i]; let next = node.get(segment); if (!next) { next = new Map(); node.set(segment, next); }
node = next; }
node[internals.value] = value; }
get(path) {
const node = this.node(path); if (node) { return node[internals.value]; } }
node(path) {
if (!this._values) { return; }
return Reach(this._values, path, { iterables: true }); }
override(path, node) {
if (!this._values) { return; }
const parents = path.slice(0, -1); const own = path[path.length - 1]; const parent = Reach(this._values, parents, { iterables: true });
if (node) { parent.set(own, node); return; }
if (parent) { parent.delete(own); } } };
|