..
Viewing
unique.ts
121 lines (106 loc) • 3.5 KB
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 | // global results store
// currently uniqueness is global to entire faker instance
// this means that faker should currently *never* return duplicate values across all API methods when using `Faker.unique`
// it's possible in the future that some users may want to scope found per function call instead of faker instance
const found: Record<string, string> = {};
// global exclude list of results
// defaults to nothing excluded
const exclude: string[] = [];
// current iteration or retries of unique.exec ( current loop depth )
const currentIterations = 0;
// uniqueness compare function
// default behavior is to check value as key against object hash
function defaultCompare<T, Key extends keyof T>(obj: T, key: Key): 0 | -1 {
if (typeof obj[key] === 'undefined') {
return -1;
}
return 0;
}
// common error handler for messages
function errorMessage(
now: number,
code: string,
opts: { startTime: number }
): never {
console.error('error', code);
console.log(
'found',
Object.keys(found).length,
'unique entries before throwing error. \nretried:',
currentIterations,
'\ntotal time:',
now - opts.startTime,
'ms'
);
throw new Error(
code +
' for uniqueness check \n\nMay not be able to generate any more unique values with current settings. \nTry adjusting maxTime or maxRetries parameters for faker.unique()'
);
}
// TODO @Shinigami92 2022-01-24: We should investigate deeper into the types
// Especially the `opts.compare` parameter and `Result` type
export function exec<Method extends (args: Args) => string, Args extends any[]>(
method: Method,
args: Args,
opts: {
maxTime?: number;
maxRetries?: number;
exclude?: string | string[];
compare?: (obj: Record<string, string>, key: string) => 0 | -1;
currentIterations?: number;
startTime?: number;
}
): string {
const now = new Date().getTime();
opts = opts || {};
opts.maxTime = opts.maxTime || 3;
opts.maxRetries = opts.maxRetries || 50;
opts.exclude = opts.exclude || exclude;
opts.compare = opts.compare || defaultCompare;
if (typeof opts.currentIterations !== 'number') {
opts.currentIterations = 0;
}
if (typeof opts.startTime === 'undefined') {
opts.startTime = new Date().getTime();
}
const startTime = opts.startTime;
// support single exclude argument as string
if (typeof opts.exclude === 'string') {
opts.exclude = [opts.exclude];
}
if (opts.currentIterations > 0) {
// console.log('iterating', currentIterations)
}
// console.log(now - startTime)
if (now - startTime >= opts.maxTime) {
return errorMessage(
now,
'Exceeded maxTime:' + opts.maxTime,
// @ts-expect-error: we know that opts.startTime is defined
opts
);
}
if (opts.currentIterations >= opts.maxRetries) {
return errorMessage(
now,
'Exceeded maxRetries:' + opts.maxRetries,
// @ts-expect-error: we know that opts.startTime is defined
opts
);
}
// execute the provided method to find a potential satisfied value
const result: string = method.apply(this, args);
// if the result has not been previously found, add it to the found array and return the value as it's unique
if (
opts.compare(found, result) === -1 &&
opts.exclude.indexOf(result) === -1
) {
found[result] = result;
opts.currentIterations = 0;
return result;
} else {
// console.log('conflict', result);
opts.currentIterations++;
return exec(method, args, opts);
}
}
|
|