-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathstreamScanner.ts
92 lines (81 loc) · 2.96 KB
/
streamScanner.ts
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
import * as stream from 'stream';
export class StreamScanner {
private _stream: stream.Readable;
private _scanCallback: (data: string, stop: () => void) => void;
constructor(stream: stream.Readable, scanCallback: (data: string, stop: () => void) => void) {
this._stream = stream;
this._scanCallback = scanCallback;
this._stream.on("data", this.scan.bind(this));
}
public stop() {
this._stream.removeListener("data", this.scan);
}
private scan(data: string | Buffer): void {
this._scanCallback(data.toString(), this.stop);
}
}
export type MatchFound = {
chunk: string,
matches: RegExpMatchArray | number[]
};
type MatchMeta = {
promise: Promise<MatchFound>,
resolve: (match: MatchFound) => void,
reject: (err: Error) => void,
test: string | RegExp
};
export class StringMatchingScanner extends StreamScanner {
private _metas: MatchMeta[];
constructor(stream: stream.Readable) {
super(stream, (data: string, stop: () => void) => {
this._metas.forEach((meta, metaIndex) => {
if (meta.test instanceof RegExp) {
let result: RegExpMatchArray = data.match(<RegExp>meta.test);
if (result && result.length > 0) {
this.matchFound(metaIndex, { chunk: data, matches: result });
}
}
else if (typeof meta.test === 'string') {
let result: number[] = []; // matches indices
let dataIndex = -1;
while((dataIndex = data.indexOf(<string>meta.test, dataIndex + 1)) > -1) {
result.push(dataIndex);
}
if (result.length > 0) {
this.matchFound(metaIndex, { chunk: data, matches: result });
}
}
else {
throw new TypeError("Invalid type");
}
});
});
this._metas = [];
}
public onEveryMatch(test: string | RegExp, handler: (result: MatchFound) => void) {
let handlerWrapper = (result: MatchFound) => {
handler(result);
this.nextMatch(test).then(handlerWrapper);
};
this.nextMatch(test).then(handlerWrapper);
}
public nextMatch(test: string | RegExp): Promise<MatchFound> {
let meta: MatchMeta = {
test: test,
resolve: null,
reject: null,
promise: null
};
meta.promise = new Promise<MatchFound>((resolve, reject) => {
meta.resolve = resolve;
meta.reject = reject;
});
this._metas.push(meta);
return meta.promise;
}
private matchFound(matchMetaIndex: number, matchResult: MatchFound) {
let meta: MatchMeta = this._metas[matchMetaIndex];
this._metas.splice(matchMetaIndex, 1); // remove the meta
meta.resolve(matchResult);
}
}