All files / tao/src AppCtxRoot.js

100% Statements 30/30
100% Branches 60/60
100% Functions 16/16
100% Lines 30/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177                                              1282x                             484x 484x 484x         414x         336x         359x         328x                     702x                     63x 63x 63x 63x 63x                                     8x                                   78x     78x 16x   62x 32x     30x   30x   30x 30x         440x         394x         364x         370x         41x                   4x      
import { WILDCARD } from './constants';
 
/**
 * A trigram names an Application Context: the thing (term), the operation on
 * it (action), and the perspective of the interaction (orient). Short
 * (`t`/`a`/`o`) and long (`term`/`action`/`orient`) keys are interchangeable
 * — supply one style per part. A missing, empty, or `'*'` part is a wildcard.
 *
 * @typedef {Object} Trigram
 * @property {string} [t] - the term (short key)
 * @property {string} [term] - the term: the domain thing
 * @property {string} [a] - the action (short key)
 * @property {string} [action] - the action: the operation on the term
 * @property {string} [o] - the orient (short key)
 * @property {string} [orient] - the orient: perspective / role / surface
 */
 
/**
 * Whether a single trigram part is wild (missing, empty, or the WILDCARD).
 * @param {string} [val] - the trigram part
 * @returns {boolean}
 */
function isPartWild(val) {
  return !val || WILDCARD === val;
}
 
/**
 * Base of the trigram-keyed classes: holds the three parts (missing parts
 * become WILDCARD) and answers wildcard / matching questions. Key format:
 * `` `${term}|${action}|${orient}` ``.
 */
export default class AppCtxRoot {
  /**
   * @param {string} [term] - the term (missing = WILDCARD)
   * @param {string} [action] - the action (missing = WILDCARD)
   * @param {string} [orient] - the orient (missing = WILDCARD)
   */
  constructor(term, action, orient) {
    this._term = term || WILDCARD;
    this._action = action || WILDCARD;
    this._orient = orient || WILDCARD;
  }
 
  /** The `term|action|orient` key for this trigram. @returns {string} */
  get key() {
    return AppCtxRoot.getKey(this._term, this._action, this._orient);
  }
 
  /** The term. @returns {string} */
  get t() {
    return this._term;
  }
 
  /** The action. @returns {string} */
  get a() {
    return this._action;
  }
 
  /** The orient. @returns {string} */
  get o() {
    return this._orient;
  }
 
  /**
   * Build the `term|action|orient` key; missing parts become WILDCARD.
   * @param {string} [term]
   * @param {string} [action]
   * @param {string} [orient]
   * @returns {string}
   */
  static getKey(term, action, orient) {
    return `${term || WILDCARD}|${action || WILDCARD}|${orient || WILDCARD}`;
  }
 
  /**
   * Whether any part of a trigram is wild (missing, empty, or WILDCARD).
   * Both key styles are accepted; short keys win when both are present.
   * @param {Trigram} [ac]
   * @returns {boolean}
   */
  // static isWildcard({ term = WILDCARD, action = WILDCARD, orient = WILDCARD } = {}) {
  static isWildcard(ac = {}) {
    const { t, term, a, action, o, orient } = ac;
    const checkT = t || term;
    const checkA = a || action;
    const checkO = o || orient;
    return (
      !checkT ||
      !checkA ||
      !checkO ||
      !checkT.length ||
      !checkA.length ||
      !checkO.length ||
      checkT === WILDCARD ||
      checkA === WILDCARD ||
      checkO === WILDCARD
    );
  }
 
  /**
   * Whether every part of a trigram is concrete (no wildcards).
   * @param {Trigram} [ac]
   * @returns {boolean}
   */
  static isConcrete(ac = {}) {
    return !AppCtxRoot.isWildcard(ac);
  }
 
  /**
   * Whether an AppCtx(-like) matches a trigram, honoring wildcards on both
   * sides unless `exact`. NOTE: `trigram` is read with short keys
   * (`t`/`a`/`o`) only — long keys are not consulted here, so a
   * long-key-only trigram is treated as all-wild.
   *
   * @param {AppCtxRoot|Trigram} ac - the context being tested (both key styles accepted)
   * @param {{t: (string|undefined), a: (string|undefined), o: (string|undefined)}} trigram - the trigram to match against (short keys only)
   * @param {boolean} [exact=false] - require exact key equality (wildcards only match wildcards)
   * @returns {boolean}
   */
  // TODO: write TESTS for this
  static isMatch(ac, trigram, exact = false) {
    // Stryker disable next-line ConditionalExpression: re-wrapping an AppCtxRoot is observationally identical
    const root =
      ac instanceof AppCtxRoot
        ? ac
        : new AppCtxRoot(ac.t || ac.term, ac.a || ac.action, ac.o || ac.orient);
    if (root.key === AppCtxRoot.getKey(trigram.t, trigram.a, trigram.o)) {
      return true;
    }
    if (exact) {
      return false;
    }
    const matchTerm =
      root.isTermWild || isPartWild(trigram.t) || root.t === trigram.t;
    const matchAction =
      root.isActionWild || isPartWild(trigram.a) || root.a === trigram.a;
    const matchOrient =
      root.isOrientWild || isPartWild(trigram.o) || root.o === trigram.o;
    return matchTerm && matchAction && matchOrient;
  }
 
  /** Whether the term is wild. @returns {boolean} */
  get isTermWild() {
    return isPartWild(this._term);
  }
 
  /** Whether the action is wild. @returns {boolean} */
  get isActionWild() {
    return isPartWild(this._action);
  }
 
  /** Whether the orient is wild. @returns {boolean} */
  get isOrientWild() {
    return isPartWild(this._orient);
  }
 
  /** Whether any part is wild. @returns {boolean} */
  get isWildcard() {
    return this.isTermWild || this.isActionWild || this.isOrientWild;
  }
 
  /** Whether every part is concrete. @returns {boolean} */
  get isConcrete() {
    return !this.isWildcard;
  }
 
  /**
   * Instance form of {@link AppCtxRoot.isMatch} with this as the context.
   * @param {{t: (string|undefined), a: (string|undefined), o: (string|undefined)}} trigram - the trigram to match against (short keys only)
   * @param {boolean} [exact=false]
   * @returns {boolean}
   */
  isMatch(trigram, exact = false) {
    return AppCtxRoot.isMatch(this, trigram, exact);
  }
}