All files / src/api AutoClose.ts

100% Statements 28/28
100% Branches 10/10
100% Functions 10/10
100% Lines 27/27

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 17027x 27x 27x                                                                                                                       27x                                                                   1179x                 27x 3643x     1168x                   27x 2358x 1179x   1179x 1168x   11x                 27x 1891x                 27x 521x               27x 1875x 1873x 1353x 520x 2x 519x 518x   1x            
import { OptionalType, RequiredType, guardFunctions, isNotPresent } from "@jonloucks/contracts-ts/api/Types";
import { presentCheck } from "@jonloucks/contracts-ts/auxiliary/Checks";
import { IllegalArgumentException } from "@jonloucks/contracts-ts/auxiliary/IllegalArgumentException";
 
/**
 * Type alias for AutoClose or a simple close function.
 */
export type AutoCloseType = AutoClose | Close | (() => unknown);
 
/**
 * Interface for a closeable resource.
 */
export interface Close {
  /**
   * Close this instance.
   */
  close(): void;
}
 
/**
 * Opt-in interface for resources that need cleanup when their lifecycle ends. For example, this is when threads should be stopped or hooks removed.
 
 * Features like life cycle promisors
 * will automatically call this method once if the deliverable implements this method.
 */
export interface AutoClose extends Close {
  /**
   * Dispose this instance.
   */
  [Symbol.dispose](): void;
}
 
/**
 * Interface for managing many closeable resources.
 */
export interface AutoCloseMany extends AutoClose {
 
  /**
   * Add a closeable resource to the list.
   * Note: there are no guards against adding the same resource multiple times.
   * 
   * @param closeable the closeable resource to add
   */
  add(closeable: RequiredType<AutoCloseType>): void;
}
 
/**
 * Interface for a single closeable resource.
 */
export interface AutoCloseOne extends AutoClose {
 
  /**
   * Add a closeable resource to the list.
   * 
   * @param closeable the closeable resource to add or null to clear
   */
  set(closeable: OptionalType<AutoCloseType>): void;
}
 
/**
 * A no-op AutoClose instance that does nothing on close or dispose.
 */
export const AUTO_CLOSE_NONE: AutoClose = {
 
  /**
   * AutoClose.close override
   */
  close: () => {
    // no-op
  },
 
  /**
   * AutoClose[Symbol.dispose] override
   */
  [Symbol.dispose]: () => {
    // no-op
  }
};
 
/**
 * Used to adapt from AutoCloseType to AutoClose with unwrapping capability.
 */
export interface AutoCloseWrapper extends AutoClose {
 
  /**
   * Unwrap to get the original AutoCloseType.
   */
  unwrapAutoCloseType(): RequiredType<AutoCloseType>;
}
 
/**
 * Check if an instance is an AutoCloseWrapper
 * @param instance the instance to check
 * @returns true if the instance is an AutoCloseWrapper, false otherwise
 */
function guardAutoCloseWrapper(instance: unknown): instance is RequiredType<AutoCloseWrapper> {
  return guardFunctions(instance, 'unwrapAutoCloseType');
}
 
/**
 * Convert a simple runnable into an AutoClose with dispose
 * 
 * @param action the runnable action to perform on close/dispose
 * @returns the AutoClose instance
 */
export function inlineAutoClose(action: () => void): RequiredType<AutoCloseWrapper> {
  return {
    close: action,
    [Symbol.dispose]: action,
    unwrapAutoCloseType: () => action
  };
}
 
/**
 * Unwrap an AutoClose to get the original type.
 * 
 * @param autoClose the AutoClose to unwrap
 * @returns the original AutoCloseType
 */
export function unwrapAutoClose(autoClose: OptionalType<AutoClose>): OptionalType<AutoCloseType> {
  if (isNotPresent(autoClose)) {
    return autoClose;
  }
  if (guardAutoCloseWrapper(autoClose)) {
    return autoClose.unwrapAutoCloseType();
  }
  return autoClose;
}
 
/**
 * Duck-typing check for AutoClose interface.
 * 
 * @param instance the instance to check
 * @returns true if the instance implements AutoClose, false otherwise
 */
export function guard(instance: unknown): instance is RequiredType<AutoClose> {
  return guardFunctions(instance, 'close', Symbol.dispose);
}
 
/**
 * Duck-typing check for object with close() method.
 * 
 * @param instance the instance to check
 * @returns true if the instance implements AutoClose, false otherwise
 */
export function isClose(instance: unknown): instance is RequiredType<AutoClose> {
  return guardFunctions(instance, 'close');
}
 
/**
 * Convert an AutoCloseType to an AutoClose
 * @param type the type to convert
 * @returns the AutoClose
 */
export function typeToAutoClose(type: RequiredType<AutoCloseType>): RequiredType<AutoClose> {
  const presentType = presentCheck(type, "AutoClose type must be present.");
  if (guard(presentType)) {
    return presentType;
  } else if (isClose(presentType)) {
    return inlineAutoClose(() => presentType.close());
  } else if (typeof presentType === 'function') {
    return inlineAutoClose(presentType as () => unknown);
  } else {
    throw new IllegalArgumentException("Invalid AutoClose type.");
  }
}