aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/cargo.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/cargo.ts')
-rw-r--r--editors/code/src/cargo.ts106
1 files changed, 106 insertions, 0 deletions
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
new file mode 100644
index 000000000..a328ba9bd
--- /dev/null
+++ b/editors/code/src/cargo.ts
@@ -0,0 +1,106 @@
1import * as cp from 'child_process';
2import * as readline from 'readline';
3import { OutputChannel } from 'vscode';
4
5interface CompilationArtifact {
6 fileName: string;
7 name: string;
8 kind: string;
9 isTest: boolean;
10}
11
12export class Cargo {
13 rootFolder: string;
14 env?: Record<string, string>;
15 output: OutputChannel;
16
17 public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record<string, string> | undefined = undefined) {
18 this.rootFolder = cargoTomlFolder;
19 this.output = output;
20 this.env = env;
21 }
22
23 public async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> {
24 const artifacts: CompilationArtifact[] = [];
25
26 try {
27 await this.runCargo(cargoArgs,
28 message => {
29 if (message.reason === 'compiler-artifact' && message.executable) {
30 const isBinary = message.target.crate_types.includes('bin');
31 const isBuildScript = message.target.kind.includes('custom-build');
32 if ((isBinary && !isBuildScript) || message.profile.test) {
33 artifacts.push({
34 fileName: message.executable,
35 name: message.target.name,
36 kind: message.target.kind[0],
37 isTest: message.profile.test
38 });
39 }
40 }
41 else if (message.reason === 'compiler-message') {
42 this.output.append(message.message.rendered);
43 }
44 },
45 stderr => {
46 this.output.append(stderr);
47 }
48 );
49 }
50 catch (err) {
51 this.output.show(true);
52 throw new Error(`Cargo invocation has failed: ${err}`);
53 }
54
55 return artifacts;
56 }
57
58 public async executableFromArgs(args: string[]): Promise<string> {
59 const cargoArgs = [...args]; // to remain args unchanged
60 cargoArgs.push("--message-format=json");
61
62 const artifacts = await this.artifactsFromArgs(cargoArgs);
63
64 if (artifacts.length === 0) {
65 throw new Error('No compilation artifacts');
66 } else if (artifacts.length > 1) {
67 throw new Error('Multiple compilation artifacts are not supported.');
68 }
69
70 return artifacts[0].fileName;
71 }
72
73 runCargo(
74 cargoArgs: string[],
75 onStdoutJson: (obj: any) => void,
76 onStderrString: (data: string) => void
77 ): Promise<number> {
78 return new Promise<number>((resolve, reject) => {
79 const cargo = cp.spawn('cargo', cargoArgs, {
80 stdio: ['ignore', 'pipe', 'pipe'],
81 cwd: this.rootFolder,
82 env: this.env,
83 });
84
85 cargo.on('error', err => {
86 reject(new Error(`could not launch cargo: ${err}`));
87 });
88 cargo.stderr.on('data', chunk => {
89 onStderrString(chunk.toString());
90 });
91
92 const rl = readline.createInterface({ input: cargo.stdout });
93 rl.on('line', line => {
94 const message = JSON.parse(line);
95 onStdoutJson(message);
96 });
97
98 cargo.on('exit', (exitCode, _) => {
99 if (exitCode === 0)
100 resolve(exitCode);
101 else
102 reject(new Error(`exit code: ${exitCode}.`));
103 });
104 });
105 }
106} \ No newline at end of file