All files / sheaves/src match.ts

94.11% Statements 16/17
93.33% Branches 14/15
100% Functions 3/3
94.11% Lines 16/17

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                                      5x         133x 133x 6x   127x 127x     127x   127x 133x 133x                                               5x         69x 131x 131x 1x   130x      
/**
 * Filter providers by guard matching at an invocation point.
 */
 
import { GET_INTERFACE_GUARD } from '@endo/exo';
import { matches } from '@endo/patterns';
import type { InterfaceGuard } from '@endo/patterns';
 
import { getInterfaceMethodGuards, getMethodPayload } from './guard.ts';
import type { Section } from './types.ts';
 
/**
 * Check whether an interface guard covers the invocation point (method, args).
 *
 * @param guard - The interface guard to test.
 * @param method - The method name being invoked.
 * @param args - The arguments to the method invocation.
 * @returns True if the guard accepts the invocation.
 */
export const guardCoversPoint = (
  guard: InterfaceGuard,
  method: string,
  args: unknown[],
): boolean => {
  const methodGuards = getInterfaceMethodGuards(guard);
  if (!(method in methodGuards)) {
    return false;
  }
  const methodGuard = methodGuards[method];
  Iif (!methodGuard) {
    return false;
  }
  const { argGuards, optionalArgGuards, restArgGuard } =
    getMethodPayload(methodGuard);
  const optionals = optionalArgGuards ?? [];
  const maxFixedArgs = argGuards.length + optionals.length;
  return (
    args.length >= argGuards.length &&
    (restArgGuard !== undefined || args.length <= maxFixedArgs) &&
    args
      .slice(0, argGuards.length)
      .every((arg, i) => matches(arg, argGuards[i])) &&
    args
      .slice(argGuards.length, maxFixedArgs)
      .every((arg, i) => matches(arg, optionals[i])) &&
    (restArgGuard === undefined ||
      args.slice(maxFixedArgs).every((arg) => matches(arg, restArgGuard)))
  );
};
 
/**
 * Get the matching providers at an invocation point.
 *
 * Returns the providers whose guards accept the given method + args.
 *
 * @param providers - The providers to filter.
 * @param method - The method name being invoked.
 * @param args - The arguments to the method invocation.
 * @returns The providers whose guards accept the invocation.
 */
export const getMatchingProviders = <T extends { exo: Section }>(
  providers: readonly T[],
  method: string,
  args: unknown[],
): T[] => {
  return providers.filter(({ exo }) => {
    const interfaceGuard = exo[GET_INTERFACE_GUARD]?.();
    if (!interfaceGuard) {
      return false;
    }
    return guardCoversPoint(interfaceGuard, method, args);
  });
};