@webex/common
Version:
Common utilities for Cisco Webex
223 lines (177 loc) • 4.28 kB
JavaScript
/*!
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
*/
import util from 'util';
/**
* Factory which produces a multi-keyed container based on the provided set of
* constructors
* @param {mixed} containers
* @returns {Container}
*/
function make(...containers) {
const TopContainer = containers.shift();
const data = new WeakMap();
const sizes = new WeakMap();
const ChildContainer = containers.length > 1 ? make(...containers) : containers[0];
const name = `(${[TopContainer.name]
.concat(containers.map((container) => container.name))
.join(', ')})`;
/**
* Container that wraps an arbitrary set of tupples to their values
*/
class Container {
/**
* @constructs Container
*/
constructor(...args) {
data.set(this, new TopContainer(...args));
sizes.set(this, 0);
}
/**
* getter for .size
* @returns {number}
*/
get size() {
return sizes.get(this);
}
/**
* Identical to Container#set() but leads slightly more intuitive code when
* the container is based on a Set rather than a Map.
* @returns {Container}
*/
add(...args) {
return this.set(...args);
}
/**
* Removes all items from the container
* @returns {undefined}
*/
clear() {
const ret = data.get(this).clear();
sizes.set(this, 0);
return ret;
}
/**
* Removes the specified item to the container
* @param {mixed} key
* @param {Array<mixed>} keys
* @returns {boolean}
*/
delete(key, ...keys) {
const mine = data.get(this);
if (!keys.length) {
return mine.delete(key);
}
const next = mine.get(key);
if (!next) {
return false;
}
const ret = next.delete(...keys);
if (ret) {
sizes.set(this, sizes.get(this) - 1);
}
if (next.size === 0) {
mine.delete(key);
}
return ret;
}
/**
* Retrieves the specified item from the container
* @param {mixed} key
* @param {Array<mixed>} keys
* @returns {mixed}
*/
get(key, ...keys) {
const mine = data.get(this);
if (!mine.get) {
return mine;
}
if (!keys.length) {
return mine.get(key);
}
const next = mine.get(key);
if (!next) {
return undefined;
}
if (!next.get) {
return next;
}
return next.get(...keys);
}
/**
* Indicates whether the container holds the specified item
* @param {mixed} key
* @param {Array<mixed>} keys
* @returns {Boolean}
*/
has(...args) {
return typeof this.get(...args) !== 'undefined';
}
/**
* Stores the specified item in the container
* @param {mixed} key
* @param {Array<mixed>} args
* @param {mixed} value
* @returns {Container}
*/
set(...args) {
let overwrite = false;
if (this.has(...args)) {
overwrite = true;
}
const mine = data.get(this);
const key = args.shift();
if (!mine.get) {
insert(mine, key, ...args);
return this;
}
let next = mine.get(key);
if (!next) {
if (!ChildContainer) {
insert(mine, key, ...args);
return this;
}
next = new ChildContainer();
insert(mine, key, next);
}
insert(next, ...args);
if (!overwrite) {
sizes.set(this, sizes.get(this) + 1);
}
return this;
}
/**
* @private
* @returns {string}
*/
inspect() {
return `Container${name} {
${util.inspect(data.get(this), {depth: null})}
}`;
}
}
return Container;
}
/**
* Inserts into an arbitrary container
* @param {Map|WeakMap|Set|WeakSet} container
* @param {Array<mixed>} args
* @private
* @returns {undefined}
*/
function insert(container, ...args) {
if (container.add) {
container.add(...args);
return;
}
if (container.set) {
container.set(...args);
return;
}
if (container.push) {
container.push(...args);
return;
}
throw new TypeError('Could not determine how to insert into the specified container');
}
export {make as default};