All files / src/api Promisor.ts

100% Statements 29/29
100% Branches 14/14
100% Functions 10/10
100% Lines 26/26

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 11426x 26x                                                                           26x 1457x                           26x 2455x 5x 2450x 993x 1457x 1211x 246x 243x   3x                                             1244x 1244x 1244x   1225x 2035x 4x                   26x 7x 2x   5x 4x   1x      
import { OptionalType, RequiredType, guardFunctions, isConstructor, isNotPresent } from "@jonloucks/contracts-ts/api/Types";
import { presentCheck } from "@jonloucks/contracts-ts/auxiliary/Checks";
 
/**
 * Interface for providing a deliverable for a Contract
 * The main and required implementation is demand().
 * There are optional methods with appropriate defaults.
 * @param <T> The type of the deliverable
 */
export interface Promisor<T> {
 
  /**
   * Return the deliverable promised for a Contract
   * @return the current deliverable
   */
  demand(): OptionalType<T>;
 
  /**
   * Reference counting used for advanced resource management
   * Incremented when bound or by other Promisors
   * Every successful 'open' must be followed by a 'close' at the appropriate time
   * @return the usage count.  This might be a constant
   */
  incrementUsage(): number;
 
  /**
   * Reference counting used for advanced resource management
   * Decremented when no longer used
   * Every successful 'open' must be followed by a 'close' at the appropriate time
   * @return the usage count.  This might be a constant
   */
  decrementUsage(): number;
}
 
/**
 * Check if an instance is a Promisor
 * @param instance the instance to check
 * @returns true if the instance is a Promisor, false otherwise
 */
export function guard<T>(instance: unknown): instance is RequiredType<Promisor<T>> {
  return guardFunctions(instance, 'demand', 'incrementUsage', 'decrementUsage');
}
 
/**
 * A type that can be converted to a Promisor
 */
export type PromisorType<T> = (new () => T) | Promisor<T> | (() => OptionalType<T>) | (() => () => T) | T | null | undefined;
 
/**
 * Convert a PromisorType to a Promisor
 * 
 * @param type the type to convert
 * @returns the Promisor
 */
export function typeToPromisor<T>(type: PromisorType<T>): RequiredType<Promisor<T>> {
  if (isNotPresent(type)) {
    return wrapPromisor<T>(type, () => type); // supplier of null or undefined
  } else if (isConstructor<T>(type)) {
    return wrapPromisor<T>(type, () => new type()); // supplier of new instance
  } else if (guard(type)) {
    return type; // already a Promisor
  } else if (typeof type === 'function') {
    return wrapPromisor<T>(type, type as () => T); 
  } else {
    return wrapPromisor<T>(type, () => type); // instance of T, runtime type-guard happens in demand
  }
}
 
/**
 * Wrapper interface for Promisor to provide unwrapping capability
 */
interface PromisorWrapper<T> extends Promisor<T> {
 
    /**
     * Unwrap to get the original Promisor.
     */
    unwrapPromisorType(): PromisorType<T>;
}
 
/**
 * Create a simple inline Promisor from a demand function
 * 
 * @param type the PromisorType
 * @param demand the demand function
 * @returns the Promisor
 */
function wrapPromisor<T>(type: PromisorType<T>, demand: () => OptionalType<T>): RequiredType<PromisorWrapper<T>> {
  const validDemand = presentCheck(demand, "Promisor demand function must be present.");
  let usageCount: number = 0; 
  return {
    demand: validDemand,
    incrementUsage: () => ++usageCount, 
    decrementUsage: () => --usageCount, 
    unwrapPromisorType: () => type
  };
}
 
/**
 * Unwrap a Promisor to get the original type.
 * 
 * @param promisor the Promisor to unwrap
 * @returns the original PromisorType
 */
export function unwrapPromisorType<T>(promisor: OptionalType<Promisor<T>>): OptionalType<PromisorType<T>> {
    if (isNotPresent(promisor)) {
        return promisor;
    }
    if ('unwrapPromisorType' in promisor && typeof promisor.unwrapPromisorType === 'function') {
        return promisor.unwrapPromisorType();
    }
    return promisor
}