1- import { Argument , AllArguments } from './Arguments'
2-
3- type ArgumentsClass = 'primitives-only' | 'with-single-argument' | 'all-arguments'
4- export class RecordedArguments {
5- private _argumentsClass : ArgumentsClass
6- private _value ?: any [ ]
7- private readonly _hasNoArguments : boolean = false
8-
9- private constructor ( rawArguments ?: any [ ] ) {
10- if ( typeof rawArguments === 'undefined' ) {
11- this . _hasNoArguments = true
12- return this
13- }
14-
15- this . _argumentsClass = this . classifyArguments ( rawArguments )
16- this . _value = rawArguments
17- }
18-
19- static from ( rawArguments : any [ ] ) : RecordedArguments {
20- return new this ( rawArguments )
21- }
22-
23- static noArguments ( ) : RecordedArguments {
24- return new this ( )
25- }
26-
27- static sort < T extends { arguments : RecordedArguments } > ( objectWithArguments : T [ ] ) : T [ ] {
28- return objectWithArguments . sort ( ( a , b ) => {
29- const aClass = a . arguments . argumentsClass
30- const bClass = b . arguments . argumentsClass
31-
32- if ( aClass === bClass ) return 0
33- if ( aClass === 'primitives-only' ) return - 1
34- if ( bClass === 'primitives-only' ) return 1
35-
36- if ( aClass === 'with-single-argument' ) return - 1
37- if ( bClass === 'with-single-argument' ) return 1
38-
39- if ( aClass === 'all-arguments' ) return - 1
40- return 1
41- } )
42- }
43-
44- get argumentsClass ( ) : ArgumentsClass {
45- return this . _argumentsClass
46- }
47-
48- get value ( ) : any [ ] | undefined {
49- return this . _value
50- }
51-
52- get hasNoArguments ( ) : boolean {
53- return this . _hasNoArguments
54- }
55-
56- public match ( other : RecordedArguments ) {
57- if ( this . hasNoArguments && other . hasNoArguments ) return true
58- if ( this . argumentsClass === 'all-arguments' || other . argumentsClass === 'all-arguments' ) return true
59- if ( this . value . length !== this . value . length ) return false
60-
61- return this . value . every ( ( argument , index ) => this . areArgumentsEqual ( argument , other . value [ index ] ) )
62- }
63-
64- private classifyArguments ( rawArguments : any [ ] ) : ArgumentsClass {
65- const allPrimitives = rawArguments . every ( arg => ! ( arg instanceof Argument || arg instanceof AllArguments ) )
66- if ( allPrimitives ) return 'primitives-only'
67-
68- const hasSingleArgument = rawArguments . some ( arg => arg instanceof Argument )
69- if ( hasSingleArgument ) return 'with-single-argument'
70-
71- return 'all-arguments'
72- }
73-
74- private areArgumentsEqual ( a : any , b : any ) : boolean {
75- if ( a instanceof Argument && b instanceof Argument ) return false
76- if ( a instanceof Argument ) return a . matches ( b )
77- if ( b instanceof Argument ) return b . matches ( a )
78- return this . areArgumentsDeepEqual ( a , b )
79- }
80-
81- private areArgumentsDeepEqual ( a : any , b : any , previousSeenObjects ?: WeakSet < object > ) : boolean {
82- const seenObjects = previousSeenObjects ?? new WeakSet ( )
83-
84- const seenA = seenObjects . has ( a )
85- const seenB = seenObjects . has ( b )
86- const aIsNonNullObject = typeof a === 'object' && a !== null
87- const bIsNonNullObject = typeof b === 'object' && b !== null
88-
89- if ( ! seenA && aIsNonNullObject ) seenObjects . add ( a )
90- if ( ! seenB && bIsNonNullObject ) seenObjects . add ( b )
91-
92- const aEqualsB = a === b
93- if ( ( seenA && seenB ) || aEqualsB ) return aEqualsB
94-
95- if ( aIsNonNullObject && bIsNonNullObject ) {
96- if ( a . constructor !== b . constructor ) return false
97- const objectAKeys = Object . keys ( a )
98- if ( objectAKeys . length !== Object . keys ( b ) . length ) return false
99- return objectAKeys . every ( key => this . areArgumentsDeepEqual ( a [ key ] , b [ key ] , seenObjects ) )
100- }
101-
102- return aEqualsB
103- }
104- }
1+ import { inspect , InspectOptions , isDeepStrictEqual } from 'util'
2+ import { Argument , AllArguments } from './Arguments'
3+
4+ type ArgumentsClass = 'plain' | 'with-predicate' | 'wildcard'
5+ export class RecordedArguments {
6+ private _argumentsClass : ArgumentsClass
7+ private _value ?: any [ ]
8+ private readonly _hasNoArguments : boolean = false
9+
10+ private constructor ( rawArguments : any [ ] | void ) {
11+ if ( typeof rawArguments === 'undefined' ) {
12+ this . _hasNoArguments = true
13+ return this
14+ }
15+
16+ this . _argumentsClass = this . classifyArguments ( rawArguments )
17+ this . _value = rawArguments
18+ }
19+
20+ static from ( rawArguments : any [ ] ) : RecordedArguments {
21+ return new this ( rawArguments )
22+ }
23+
24+ static none ( ) : RecordedArguments {
25+ return new this ( )
26+ }
27+
28+ static sort < T extends { recordedArguments : RecordedArguments } > ( objectWithArguments : T [ ] ) : T [ ] {
29+ return objectWithArguments . sort ( ( a , b ) => {
30+ const aClass = a . recordedArguments . argumentsClass
31+ const bClass = b . recordedArguments . argumentsClass
32+
33+ if ( aClass === bClass ) return 0
34+ if ( aClass === 'plain' ) return - 1
35+ if ( bClass === 'plain' ) return 1
36+
37+ if ( aClass === 'with-predicate' ) return - 1
38+ if ( bClass === 'with-predicate' ) return 1
39+
40+ if ( aClass === 'wildcard' ) return - 1
41+ return 1
42+ } )
43+ }
44+
45+ get argumentsClass ( ) : ArgumentsClass {
46+ return this . _argumentsClass
47+ }
48+
49+ get value ( ) : any [ ] | undefined {
50+ return this . _value
51+ }
52+
53+ get hasNoArguments ( ) : boolean {
54+ return this . _hasNoArguments
55+ }
56+
57+ public match ( other : RecordedArguments ) {
58+ if ( this . argumentsClass === 'wildcard' || other . argumentsClass === 'wildcard' ) return true
59+ if ( this . hasNoArguments || other . hasNoArguments ) return this . hasNoArguments && other . hasNoArguments
60+ if ( this . value . length !== other . value . length ) return false
61+
62+ return this . value . every ( ( argument , index ) => this . areArgumentsEqual ( argument , other . value [ index ] ) )
63+ }
64+
65+ private classifyArguments ( rawArguments : any [ ] ) : ArgumentsClass {
66+ const allPlain = rawArguments . every ( arg => ! ( arg instanceof Argument || arg instanceof AllArguments ) )
67+ if ( allPlain ) return 'plain'
68+
69+ const hasSingleArgument = rawArguments . some ( arg => arg instanceof Argument )
70+ if ( hasSingleArgument ) return 'with-predicate'
71+
72+ return 'wildcard'
73+ }
74+
75+ private areArgumentsEqual ( a : any , b : any ) : boolean {
76+ if ( a instanceof Argument && b instanceof Argument ) return false
77+ if ( a instanceof Argument ) return a . matches ( b )
78+ if ( b instanceof Argument ) return b . matches ( a )
79+ return isDeepStrictEqual ( a , b )
80+ }
81+
82+ [ inspect . custom ] ( _ : number , options : InspectOptions ) {
83+ return this . printableForm ( options )
84+ }
85+
86+ private printableForm ( options : InspectOptions ) : string {
87+ const inspectedValues = this . value ?. map ( v => inspect ( v , options ) )
88+ if ( ! Array . isArray ( inspectedValues ) ) return ''
89+ return inspectedValues . length !== 1
90+ ? `(${ inspectedValues . join ( ', ' ) } )`
91+ : inspectedValues [ 0 ]
92+ }
93+ }
0 commit comments