Source: objectid.js


const {notNil,def,isNil} = require('./types');

/**
 * Generate a large random number.
 * 
 * @returns {number}
 * @alias module:@lumjs/core.randomNumber
 */
function randomNumber()
{
  return Math.floor(Math.random() * Date.now());
}

exports.randomNumber = randomNumber;

/**
 * A class for creating unique identifier objects.
 * Generally only used by my own inernal libraries, thus the name.
 * @alias module:@lumjs/core.InternalObjectId
 */
class InternalObjectId
{
  /**
   * Build a unique InternalObjectId instance.
   * 
   * @param {object} opts - Named options to change default behaviours.
   * @param {string} [opts.name] A friendly name for diagnostics.
   * @param {(string|number)} [opts.id] An internal id value.
   *   Default value is a large random number. 
   * @param {(string|Symbol)} [opts.property] The property name or Symbol.
   *   This is the property that will be set on objects that are tagged
   *   with this InternalObjectId. Default is `Symbol(this.id)`.
   * @param {boolean} [opts.useInstance=true] Store object or id value?
   *   If this is `true` (now the default), we store the instance itself.
   *   If this is `false` (the old default), we store just the `id` value.
   */
  constructor(opts={})
  {
    this.name = opts.name;
    this.id = opts.id ?? randomNumber();
    this.property = opts.property ?? Symbol(this.id);
    this.useInstance = opts.useInstance ?? true; 
  }

  /**
   * Tag an object with the ObjectId.
   * 
   * @param {*} obj - The object to tag with the unique object id.
   * @returns {*} `obj`
   */
  tag(obj)
  {
    if (isNil(obj)) throw new TypeError("Cannot tag a null or undefined value");
    const val = this.useInstance ? this : this.id;
    return def(obj, this.property, val);
  }

  /**
   * Remove the tag from a tagged object.
   * 
   * @param {*} obj 
   */
  untag(obj)
  {
    if (notNil(obj))
    {
      delete(obj[this.property]);
    }
    return obj;
  }

  /**
   * Is the specified object tagged with this object id?
   * 
   * @param {*} obj - The object to test.
   * @returns {boolean} If it's tagged or not.
   */
  is(obj)
  {
    const want = this.useInstance ? this : this.id;
    return (notNil(obj) && obj[this.property] === want);
  }

  /**
   * Generate a function that calls `instance.is()`
   * 
   * @returns {function} The wrapper function.
   */
  isFunction()
  {
    const oid = this;
    return function(obj) { return oid.is(obj); }
  }

}

exports.InternalObjectId = InternalObjectId;