Skip to content

Commit

Permalink
fix(patterns): type guards
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Aug 9, 2023
1 parent db35cc7 commit a0170df
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 148 deletions.
27 changes: 13 additions & 14 deletions packages/exo/src/exo-tools.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { E, Far } from '@endo/far';
import { hasOwnPropertyOf } from '@endo/pass-style';
import { listDifference, objectMap, mustMatch, M } from '@endo/patterns';
import {
listDifference,
objectMap,
mustMatch,
M,
isAwaitArgGuard,
assertMethodGuard,
assertInterfaceGuard,
} from '@endo/patterns';

/** @typedef {import('@endo/patterns').Method} Method */
/** @typedef {import('@endo/patterns').MethodGuard} MethodGuard */
Expand Down Expand Up @@ -47,9 +55,6 @@ const defendSyncMethod = (method, methodGuard, label) => {
return syncMethod;
};

const isAwaitArgGuard = argGuard =>
argGuard && typeof argGuard === 'object' && argGuard.klass === 'awaitArg';

const desync = methodGuard => {
const { argGuards, optionalArgGuards = [], restArgGuard } = methodGuard;
!isAwaitArgGuard(restArgGuard) ||
Expand Down Expand Up @@ -106,8 +111,8 @@ const defendAsyncMethod = (method, methodGuard, label) => {
* @param {string} label
*/
const defendMethod = (method, methodGuard, label) => {
const { klass, callKind } = methodGuard;
assert(klass === 'methodGuard');
assertMethodGuard(methodGuard);
const { callKind } = methodGuard;
if (callKind === 'sync') {
return defendSyncMethod(method, methodGuard, label);
} else {
Expand Down Expand Up @@ -257,15 +262,9 @@ export const defendPrototype = (
}
let methodGuards;
if (interfaceGuard) {
const {
klass,
interfaceName,
methodGuards: mg,
sloppy = false,
} = interfaceGuard;
assertInterfaceGuard(interfaceGuard);
const { interfaceName, methodGuards: mg, sloppy = false } = interfaceGuard;
methodGuards = mg;
assert.equal(klass, 'Interface');
assert.typeof(interfaceName, 'string');
{
const methodNames = ownKeys(behaviorMethods);
const methodGuardNames = ownKeys(methodGuards);
Expand Down
4 changes: 4 additions & 0 deletions packages/patterns/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export {
assertPattern,
matches,
mustMatch,
isAwaitArgGuard,
assertAwaitArgGuard,
assertMethodGuard,
assertInterfaceGuard,
} from './src/patterns/patternMatchers.js';

// ////////////////// Temporary, until these find their proper home ////////////
Expand Down
66 changes: 66 additions & 0 deletions packages/patterns/src/patterns/internal-types.js
Original file line number Diff line number Diff line change
@@ -1 +1,67 @@
/// <reference types="ses"/>

/** @typedef {import('@endo/marshal').Passable} Passable */
/** @typedef {import('@endo/marshal').PassStyle} PassStyle */
/** @typedef {import('@endo/marshal').CopyTagged} CopyTagged */
/** @template T @typedef {import('@endo/marshal').CopyRecord<T>} CopyRecord */
/** @template T @typedef {import('@endo/marshal').CopyArray<T>} CopyArray */
/** @typedef {import('@endo/marshal').Checker} Checker */
/** @typedef {import('@endo/marshal').RankCompare} RankCompare */
/** @typedef {import('@endo/marshal').RankCover} RankCover */

/** @typedef {import('../types.js').AwaitArgGuard} AwaitArgGuard */
/** @typedef {import('../types.js').ArgGuard} ArgGuard */
/** @typedef {import('../types.js').MethodGuard} MethodGuard */
/** @typedef {import('../types.js').InterfaceGuard} InterfaceGuard */
/** @typedef {import('../types.js').MethodGuardMaker0} MethodGuardMaker0 */

/** @typedef {import('../types').MatcherNamespace} MatcherNamespace */
/** @typedef {import('../types').Key} Key */
/** @typedef {import('../types').Pattern} Pattern */
/** @typedef {import('../types').CheckPattern} CheckPattern */
/** @typedef {import('../types').Limits} Limits */
/** @typedef {import('../types').AllLimits} AllLimits */
/** @typedef {import('../types').GetRankCover} GetRankCover */

/**
* @typedef {object} MatchHelper
* This factors out only the parts specific to each kind of Matcher. It is
* encapsulated, and its methods can make the stated unchecked assumptions
* enforced by the common calling logic.
*
* @property {(allegedPayload: Passable,
* check: Checker
* ) => boolean} checkIsWellFormed
* Reports whether `allegedPayload` is valid as the payload of a CopyTagged
* whose tag corresponds with this MatchHelper's Matchers.
*
* @property {(specimen: Passable,
* matcherPayload: Passable,
* check: Checker,
* ) => boolean} checkMatches
* Assuming validity of `matcherPayload` as the payload of a Matcher corresponding
* with this MatchHelper, reports whether `specimen` is matched by that Matcher.
*
* @property {import('../types').GetRankCover} getRankCover
* Assumes this is the payload of a CopyTagged with the corresponding
* matchTag. Return a RankCover to bound from below and above,
* in rank order, all possible Passables that would match this Matcher.
* The left element must be before or the same rank as any possible
* matching specimen. The right element must be after or the same
* rank as any possible matching specimen.
*/

/**
* @typedef {object} PatternKit
* @property {(specimen: Passable,
* patt: Passable,
* check: Checker,
* label?: string|number
* ) => boolean} checkMatches
* @property {(specimen: Passable, patt: Pattern) => boolean} matches
* @property {(specimen: Passable, patt: Pattern, label?: string|number) => void} mustMatch
* @property {(patt: Pattern) => void} assertPattern
* @property {(patt: Passable) => boolean} isPattern
* @property {GetRankCover} getRankCover
* @property {MatcherNamespace} M
*/
Loading

0 comments on commit a0170df

Please sign in to comment.