-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.ts
executable file
·107 lines (101 loc) · 2.64 KB
/
main.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env -S deno run --allow-import=jsr.io:443
import { Command } from "./deps/cliffy/command.ts";
import { joinToString } from "./deps/std/collections/join_to_string.ts";
import {
beatToBar,
checkLevel,
formatTime,
parseLevel,
timeToBeat,
} from "./mod.ts";
const encoder = new TextEncoder();
const decoder = new TextDecoder(undefined, { fatal: true });
function warn(message: string): undefined {
console.warn("%cWarning%c", "color: yellow", "", message);
}
const descs = [
["invalidCues", "Invalid cue"],
["unexpectedSkipshots", "Unexpected skipshot"],
["overlappingSkipshots", "Overlapping skipshots"],
["unexpectedFreezeshots", "Unexpected freezeshot"],
["overlappingFreezeshots", "Overlapping freezeshots"],
["unexpectedBurnshots", "Unexpected burnshots"],
["overlappingBurnshots", "Overlapping burnshots"],
["uncuedHits", "Uncued hit"],
["skippedHits", "Skipped hit"],
["missingHits", "Missing hit"],
["overlappingHolds", "Overlapping holds"],
] as const;
const {
options: { ignoreSource, interruptiblePattern, triangleshot },
cmd,
} = await new Command()
.name("rd-nurse")
.usage("[options]")
.description(`
Checks a Rhythm Doctor level for illegal oneshots and holds.
The level is read from stdin.
`)
.option(
"-s, --ignore-source",
"Ignore the voice sources of the cues.",
)
.option(
"-p, --interruptible-pattern",
"Make squareshots stop oneshot patterns.",
)
.option(
"-t, --triangleshot",
"Enable triangleshots.",
)
.error((error, cmd) => {
cmd.showHelp();
console.error(
"%cerror%c:",
"color: red; font-weight: bold",
"",
error.message,
);
Deno.exit(2);
})
.parse();
if (Deno.stdin.isTerminal()) {
warn("Reading from stdin which is a terminal");
}
const input = await new Response(Deno.stdin.readable).bytes();
if (input.length === 0) {
cmd.showHelp();
Deno.exit(2);
}
const level = parseLevel(decoder.decode(input));
const result = checkLevel(level, {
ignoreSource,
interruptiblePattern,
triangleshot,
});
if (result.hasBurnshot) {
warn("Level contains burnshots; results may be incorrect");
}
const { cpbChanges, tempoChanges } = level;
let count = 0;
let output = "";
for (const [key, desc] of descs) {
const pos = result[key];
if (pos.length === 0) {
continue;
}
count += pos.length;
output += `${desc}: ${
joinToString(
pos,
(time) =>
formatTime(beatToBar(cpbChanges, timeToBeat(tempoChanges, time))),
{ separator: ", " },
)
}\n`;
}
await ReadableStream.from([encoder.encode(output)])
.pipeTo(Deno.stdout.writable).catch(() => {});
if (count !== 0) {
Deno.exit(1);
}