@git.zone/tsrust 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ process.env.CLI_CALL = 'true';
3
+ const cliTool = await import('./dist_ts/index.js');
4
+ cliTool.runCli();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export declare const commitinfo: {
5
+ name: string;
6
+ version: string;
7
+ description: string;
8
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@git.zone/tsrust',
6
+ version: '1.0.2',
7
+ description: 'A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.'
8
+ };
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxrQkFBa0I7SUFDeEIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLGtKQUFrSjtDQUNoSyxDQUFBIn0=
@@ -0,0 +1,3 @@
1
+ export * from './mod_fs/index.js';
2
+ export * from './mod_cargo/index.js';
3
+ export * from './mod_cli/index.js';
@@ -0,0 +1,7 @@
1
+ import * as plugins from './plugins.js';
2
+ plugins.early.start('@git.zone/tsrust');
3
+ export * from './mod_fs/index.js';
4
+ export * from './mod_cargo/index.js';
5
+ export * from './mod_cli/index.js';
6
+ plugins.early.stop();
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0FBRXhDLGNBQWMsbUJBQW1CLENBQUM7QUFDbEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLG9CQUFvQixDQUFDO0FBRW5DLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMifQ==
@@ -0,0 +1,12 @@
1
+ export interface ICargoWorkspaceInfo {
2
+ isWorkspace: boolean;
3
+ rustDir: string;
4
+ binTargets: string[];
5
+ }
6
+ export declare class CargoConfig {
7
+ private rustDir;
8
+ constructor(rustDir: string);
9
+ parse(): Promise<ICargoWorkspaceInfo>;
10
+ private collectWorkspaceBinTargets;
11
+ private collectCrateBinTargets;
12
+ }
@@ -0,0 +1,71 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import * as smolToml from 'smol-toml';
4
+ import { FsHelpers } from '../mod_fs/index.js';
5
+ export class CargoConfig {
6
+ rustDir;
7
+ constructor(rustDir) {
8
+ this.rustDir = rustDir;
9
+ }
10
+ async parse() {
11
+ const cargoTomlPath = path.join(this.rustDir, 'Cargo.toml');
12
+ const content = await fs.promises.readFile(cargoTomlPath, 'utf-8');
13
+ const parsed = smolToml.parse(content);
14
+ const isWorkspace = !!parsed.workspace;
15
+ let binTargets = [];
16
+ if (isWorkspace) {
17
+ binTargets = await this.collectWorkspaceBinTargets(parsed);
18
+ }
19
+ else {
20
+ binTargets = this.collectCrateBinTargets(parsed, this.rustDir);
21
+ }
22
+ return {
23
+ isWorkspace,
24
+ rustDir: this.rustDir,
25
+ binTargets,
26
+ };
27
+ }
28
+ async collectWorkspaceBinTargets(parsed) {
29
+ const members = parsed.workspace?.members || [];
30
+ const binTargets = [];
31
+ for (const member of members) {
32
+ const memberDir = path.join(this.rustDir, member);
33
+ const memberCargoToml = path.join(memberDir, 'Cargo.toml');
34
+ if (!(await FsHelpers.fileExists(memberCargoToml))) {
35
+ continue;
36
+ }
37
+ const memberContent = await fs.promises.readFile(memberCargoToml, 'utf-8');
38
+ const memberParsed = smolToml.parse(memberContent);
39
+ const memberBins = this.collectCrateBinTargets(memberParsed, memberDir);
40
+ binTargets.push(...memberBins);
41
+ }
42
+ return binTargets;
43
+ }
44
+ collectCrateBinTargets(parsed, crateDir) {
45
+ const binTargets = [];
46
+ // Check for explicit [[bin]] entries
47
+ if (Array.isArray(parsed.bin)) {
48
+ for (const bin of parsed.bin) {
49
+ if (bin.name) {
50
+ binTargets.push(bin.name);
51
+ }
52
+ }
53
+ }
54
+ // If no [[bin]] but package has a name and src/main.rs exists, use package name
55
+ if (binTargets.length === 0 && parsed.package?.name) {
56
+ const mainRsPath = path.join(crateDir, 'src', 'main.rs');
57
+ // Use sync check since this is called during parsing
58
+ try {
59
+ const stat = fs.statSync(mainRsPath);
60
+ if (stat.isFile()) {
61
+ binTargets.push(parsed.package.name);
62
+ }
63
+ }
64
+ catch {
65
+ // No main.rs, not a binary crate
66
+ }
67
+ }
68
+ return binTargets;
69
+ }
70
+ }
71
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jYXJnb2NvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9jYXJnby9jbGFzc2VzLmNhcmdvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQVEvQyxNQUFNLE9BQU8sV0FBVztJQUNkLE9BQU8sQ0FBUztJQUV4QixZQUFZLE9BQWU7UUFDekIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVNLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxZQUFZLENBQUMsQ0FBQztRQUM1RCxNQUFNLE9BQU8sR0FBRyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNuRSxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRXZDLE1BQU0sV0FBVyxHQUFHLENBQUMsQ0FBRSxNQUFjLENBQUMsU0FBUyxDQUFDO1FBQ2hELElBQUksVUFBVSxHQUFhLEVBQUUsQ0FBQztRQUU5QixJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ2hCLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM3RCxDQUFDO2FBQU0sQ0FBQztZQUNOLFVBQVUsR0FBRyxJQUFJLENBQUMsc0JBQXNCLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNqRSxDQUFDO1FBRUQsT0FB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
@@ -0,0 +1,17 @@
1
+ export interface ICargoRunResult {
2
+ success: boolean;
3
+ exitCode: number;
4
+ stdout: string;
5
+ }
6
+ export declare class CargoRunner {
7
+ private shell;
8
+ private rustDir;
9
+ constructor(rustDir: string);
10
+ checkCargoInstalled(): Promise<boolean>;
11
+ getCargoVersion(): Promise<string>;
12
+ build(options?: {
13
+ debug?: boolean;
14
+ clean?: boolean;
15
+ }): Promise<ICargoRunResult>;
16
+ clean(): Promise<ICargoRunResult>;
17
+ }
@@ -0,0 +1,44 @@
1
+ import * as plugins from '../plugins.js';
2
+ export class CargoRunner {
3
+ shell;
4
+ rustDir;
5
+ constructor(rustDir) {
6
+ this.rustDir = rustDir;
7
+ this.shell = new plugins.smartshell.Smartshell({
8
+ executor: 'bash',
9
+ });
10
+ }
11
+ async checkCargoInstalled() {
12
+ const result = await this.shell.execSilent('cargo --version');
13
+ return result.exitCode === 0;
14
+ }
15
+ async getCargoVersion() {
16
+ const result = await this.shell.execSilent('cargo --version');
17
+ return result.stdout.trim();
18
+ }
19
+ async build(options = {}) {
20
+ if (options.clean) {
21
+ console.log('Cleaning previous build...');
22
+ await this.clean();
23
+ }
24
+ const profile = options.debug ? '' : ' --release';
25
+ const command = `cd ${this.rustDir} && cargo build${profile}`;
26
+ console.log(`Running: cargo build${profile}`);
27
+ const result = await this.shell.exec(command);
28
+ return {
29
+ success: result.exitCode === 0,
30
+ exitCode: result.exitCode,
31
+ stdout: result.stdout,
32
+ };
33
+ }
34
+ async clean() {
35
+ const command = `cd ${this.rustDir} && cargo clean`;
36
+ const result = await this.shell.exec(command);
37
+ return {
38
+ success: result.exitCode === 0,
39
+ exitCode: result.exitCode,
40
+ stdout: result.stdout,
41
+ };
42
+ }
43
+ }
44
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jYXJnb3J1bm5lci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9jYXJnby9jbGFzc2VzLmNhcmdvcnVubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBUXpDLE1BQU0sT0FBTyxXQUFXO0lBQ2QsS0FBSyxDQUFnQztJQUNyQyxPQUFPLENBQVM7SUFFeEIsWUFBWSxPQUFlO1FBQ3pCLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQztZQUM3QyxRQUFRLEVBQUUsTUFBTTtTQUNqQixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sS0FBSyxDQUFDLG1CQUFtQjtRQUM5QixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDOUQsT0FBTyxNQUFNLENBQUMsUUFBUSxLQUFLLENBQUMsQ0FBQztJQUMvQixDQUFDO0lBRU0sS0FBSyxDQUFDLGVBQWU7UUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzlELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFnRCxFQUFFO1FBQ25FLElBQUksT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztZQUMxQyxNQUFNLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNyQixDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7UUFDbEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxrQkFBa0IsT0FBTyxFQUFFLENBQUM7UUFFOUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM5QyxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTlDLE9BQU87WUFDTCxPQUFPLEVBQUUsTUFBTSxDQUFDLFFBQVEsS0FBSyxDQUFDO1lBQzlCLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtZQUN6QixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07U0FDdEIsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsS0FBSztRQUNoQixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLGlCQUFpQixDQUFDO1FBQ3BELE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFOUMsT0FBTztZQUNMLE9BQU8sRUFBRSxNQUFNLENBQUMsUUFBUSxLQUFLLENBQUM7WUFDOUIsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO1lBQ3pCLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtTQUN0QixDQUFDO0lBQ0osQ0FBQztDQUNGIn0=
@@ -0,0 +1,2 @@
1
+ export { CargoConfig } from './classes.cargoconfig.js';
2
+ export { CargoRunner } from './classes.cargorunner.js';
@@ -0,0 +1,3 @@
1
+ export { CargoConfig } from './classes.cargoconfig.js';
2
+ export { CargoRunner } from './classes.cargorunner.js';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfY2FyZ28vaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQyJ9
@@ -0,0 +1,11 @@
1
+ export declare class TsRustCli {
2
+ private cli;
3
+ private cwd;
4
+ constructor(cwd?: string);
5
+ private registerCommands;
6
+ private registerStandardCommand;
7
+ private registerCleanCommand;
8
+ private detectRustDir;
9
+ run(): void;
10
+ }
11
+ export declare const runCli: () => Promise<void>;
@@ -0,0 +1,118 @@
1
+ import * as path from 'path';
2
+ import * as plugins from '../plugins.js';
3
+ import { CargoConfig } from '../mod_cargo/index.js';
4
+ import { CargoRunner } from '../mod_cargo/index.js';
5
+ import { FsHelpers } from '../mod_fs/index.js';
6
+ export class TsRustCli {
7
+ cli;
8
+ cwd;
9
+ constructor(cwd = process.cwd()) {
10
+ this.cwd = cwd;
11
+ this.cli = new plugins.smartcli.Smartcli();
12
+ this.registerCommands();
13
+ }
14
+ registerCommands() {
15
+ this.registerStandardCommand();
16
+ this.registerCleanCommand();
17
+ }
18
+ registerStandardCommand() {
19
+ this.cli.standardCommand().subscribe(async (argvArg) => {
20
+ const startTime = Date.now();
21
+ // Check cargo is installed
22
+ const runner = new CargoRunner(this.cwd); // temporary, just for version check
23
+ if (!(await runner.checkCargoInstalled())) {
24
+ console.error('Error: cargo is not installed or not in PATH.');
25
+ console.error('Install Rust via https://rustup.rs/');
26
+ process.exit(1);
27
+ }
28
+ const cargoVersion = await runner.getCargoVersion();
29
+ console.log(`Using ${cargoVersion}`);
30
+ // Detect rust directory
31
+ const rustDir = await this.detectRustDir();
32
+ if (!rustDir) {
33
+ console.error('Error: No rust/ or ts_rust/ directory found with a Cargo.toml.');
34
+ process.exit(1);
35
+ }
36
+ console.log(`Found Rust project at: ${path.relative(this.cwd, rustDir) || '.'}`);
37
+ // Parse Cargo.toml
38
+ const cargoConfig = new CargoConfig(rustDir);
39
+ const workspaceInfo = await cargoConfig.parse();
40
+ if (workspaceInfo.isWorkspace) {
41
+ console.log('Detected Cargo workspace');
42
+ }
43
+ if (workspaceInfo.binTargets.length === 0) {
44
+ console.error('Error: No binary targets found in Cargo.toml.');
45
+ process.exit(1);
46
+ }
47
+ console.log(`Binary targets: ${workspaceInfo.binTargets.join(', ')}`);
48
+ // Build
49
+ const isDebug = !!argvArg.debug;
50
+ const shouldClean = !!argvArg.clean;
51
+ const cargoRunner = new CargoRunner(rustDir);
52
+ const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean });
53
+ if (!buildResult.success) {
54
+ console.error(`Build failed with exit code ${buildResult.exitCode}`);
55
+ process.exit(1);
56
+ }
57
+ // Copy binaries to dist_rust/
58
+ const profile = isDebug ? 'debug' : 'release';
59
+ const targetDir = path.join(rustDir, 'target', profile);
60
+ const distDir = path.join(this.cwd, 'dist_rust');
61
+ await FsHelpers.ensureEmptyDir(distDir);
62
+ for (const binName of workspaceInfo.binTargets) {
63
+ const srcBinary = path.join(targetDir, binName);
64
+ const destBinary = path.join(distDir, binName);
65
+ if (!(await FsHelpers.fileExists(srcBinary))) {
66
+ console.warn(`Warning: Expected binary not found: ${srcBinary}`);
67
+ continue;
68
+ }
69
+ await FsHelpers.copyFile(srcBinary, destBinary);
70
+ await FsHelpers.makeExecutable(destBinary);
71
+ const size = await FsHelpers.getFileSize(destBinary);
72
+ console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`);
73
+ }
74
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
75
+ console.log(`Done in ${elapsed}s`);
76
+ });
77
+ }
78
+ registerCleanCommand() {
79
+ this.cli.addCommand('clean').subscribe(async (_argvArg) => {
80
+ // Clean cargo build
81
+ const rustDir = await this.detectRustDir();
82
+ if (rustDir) {
83
+ console.log('Running cargo clean...');
84
+ const runner = new CargoRunner(rustDir);
85
+ await runner.clean();
86
+ console.log('Cargo clean complete.');
87
+ }
88
+ // Remove dist_rust/
89
+ const distDir = path.join(this.cwd, 'dist_rust');
90
+ if (await FsHelpers.directoryExists(distDir)) {
91
+ await FsHelpers.removeDirectory(distDir);
92
+ console.log('Removed dist_rust/');
93
+ }
94
+ console.log('Clean complete.');
95
+ });
96
+ }
97
+ async detectRustDir() {
98
+ // Check rust/ first
99
+ const rustDir = path.join(this.cwd, 'rust');
100
+ if (await FsHelpers.fileExists(path.join(rustDir, 'Cargo.toml'))) {
101
+ return rustDir;
102
+ }
103
+ // Fallback to ts_rust/
104
+ const tsRustDir = path.join(this.cwd, 'ts_rust');
105
+ if (await FsHelpers.fileExists(path.join(tsRustDir, 'Cargo.toml'))) {
106
+ return tsRustDir;
107
+ }
108
+ return null;
109
+ }
110
+ run() {
111
+ this.cli.startParse();
112
+ }
113
+ }
114
+ export const runCli = async () => {
115
+ const cli = new TsRustCli();
116
+ cli.run();
117
+ };
118
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50c3J1c3RjbGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfY2xpL2NsYXNzZXMudHNydXN0Y2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRS9DLE1BQU0sT0FBTyxTQUFTO0lBQ1osR0FBRyxDQUE0QjtJQUMvQixHQUFHLENBQVM7SUFFcEIsWUFBWSxNQUFjLE9BQU8sQ0FBQyxHQUFHLEVBQUU7UUFDckMsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsR0FBRyxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUMzQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztJQUMxQixDQUFDO0lBRU8sZ0JBQWdCO1FBQ3RCLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFTyx1QkFBdUI7UUFDN0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO1lBQ3JELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUU3QiwyQkFBMkI7WUFDM0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxXQUFXLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsb0NBQW9DO1lBQzlFLElBQUksQ0FBQyxDQUFDLE1B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
@@ -0,0 +1 @@
1
+ export { TsRustCli, runCli } from './classes.tsrustcli.js';
@@ -0,0 +1,2 @@
1
+ export { TsRustCli, runCli } from './classes.tsrustcli.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfY2xpL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUMifQ==
@@ -0,0 +1,10 @@
1
+ export declare class FsHelpers {
2
+ static fileExists(filePath: string): Promise<boolean>;
3
+ static directoryExists(dirPath: string): Promise<boolean>;
4
+ static ensureEmptyDir(dirPath: string): Promise<void>;
5
+ static copyFile(src: string, dest: string): Promise<void>;
6
+ static makeExecutable(filePath: string): Promise<void>;
7
+ static getFileSize(filePath: string): Promise<number>;
8
+ static removeDirectory(dirPath: string): Promise<void>;
9
+ static formatFileSize(bytes: number): string;
10
+ }
@@ -0,0 +1,53 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export class FsHelpers {
4
+ static async fileExists(filePath) {
5
+ try {
6
+ const stat = await fs.promises.stat(filePath);
7
+ return stat.isFile();
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ static async directoryExists(dirPath) {
14
+ try {
15
+ const stat = await fs.promises.stat(dirPath);
16
+ return stat.isDirectory();
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ static async ensureEmptyDir(dirPath) {
23
+ if (await FsHelpers.directoryExists(dirPath)) {
24
+ await fs.promises.rm(dirPath, { recursive: true, force: true });
25
+ }
26
+ await fs.promises.mkdir(dirPath, { recursive: true });
27
+ }
28
+ static async copyFile(src, dest) {
29
+ const destDir = path.dirname(dest);
30
+ await fs.promises.mkdir(destDir, { recursive: true });
31
+ await fs.promises.copyFile(src, dest);
32
+ }
33
+ static async makeExecutable(filePath) {
34
+ await fs.promises.chmod(filePath, 0o755);
35
+ }
36
+ static async getFileSize(filePath) {
37
+ const stat = await fs.promises.stat(filePath);
38
+ return stat.size;
39
+ }
40
+ static async removeDirectory(dirPath) {
41
+ if (await FsHelpers.directoryExists(dirPath)) {
42
+ await fs.promises.rm(dirPath, { recursive: true, force: true });
43
+ }
44
+ }
45
+ static formatFileSize(bytes) {
46
+ if (bytes < 1024)
47
+ return `${bytes} B`;
48
+ if (bytes < 1024 * 1024)
49
+ return `${(bytes / 1024).toFixed(1)} KB`;
50
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
51
+ }
52
+ }
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5mc2hlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfZnMvY2xhc3Nlcy5mc2hlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDekIsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFFN0IsTUFBTSxPQUFPLFNBQVM7SUFDYixNQUFNLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxRQUFnQjtRQUM3QyxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksR0FBRyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzlDLE9BQU8sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3ZCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxlQUFlLENBQUMsT0FBZTtRQUNqRCxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksR0FBRyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzdDLE9BQU8sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzVCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsT0FBZTtRQUNoRCxJQUFJLE1BQU0sU0FBUyxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQzdDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBQ0QsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBRU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBVyxFQUFFLElBQVk7UUFDcEQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFTSxNQUFNLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxRQUFnQjtRQUNqRCxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsUUFBZ0I7UUFDOUMsTUFBTSxJQUFJLEdBQUcsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM5QyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDbkIsQ0FBQztJQUVNLE1BQU0sQ0FBQyxLQUFLLENBQUMsZUFBZSxDQUFDLE9BQWU7UUFDakQsSUFBSSxNQUFNLFNBQVMsQ0FBQyxlQUFlLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUM3QyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDbEUsQ0FBQztJQUNILENBQUM7SUFFTSxNQUFNLENBQUMsY0FBYyxDQUFDLEtBQWE7UUFDeEMsSUFBSSxLQUFLLEdBQUcsSUFBSTtZQUFFLE9BQU8sR0FBRyxLQUFLLElBQUksQ0FBQztRQUN0QyxJQUFJLEtBQUssR0FBRyxJQUFJLEdBQUcsSUFBSTtZQUFFLE9BQU8sR0FBRyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztRQUNsRSxPQUFPLEdBQUcsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztJQUNwRCxDQUFDO0NBQ0YifQ==
@@ -0,0 +1 @@
1
+ export { FsHelpers } from './classes.fshelpers.js';
@@ -0,0 +1,2 @@
1
+ export { FsHelpers } from './classes.fshelpers.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfZnMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHdCQUF3QixDQUFDIn0=
@@ -0,0 +1,6 @@
1
+ import * as early from '@push.rocks/early';
2
+ import * as smartcli from '@push.rocks/smartcli';
3
+ import * as smartfile from '@push.rocks/smartfile';
4
+ import * as smartpath from '@push.rocks/smartpath';
5
+ import * as smartshell from '@push.rocks/smartshell';
6
+ export { early, smartcli, smartfile, smartpath, smartshell, };
@@ -0,0 +1,7 @@
1
+ import * as early from '@push.rocks/early';
2
+ import * as smartcli from '@push.rocks/smartcli';
3
+ import * as smartfile from '@push.rocks/smartfile';
4
+ import * as smartpath from '@push.rocks/smartpath';
5
+ import * as smartshell from '@push.rocks/smartshell';
6
+ export { early, smartcli, smartfile, smartpath, smartshell, };
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEtBQUssTUFBTSxtQkFBbUIsQ0FBQztBQUMzQyxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBRXJELE9BQU8sRUFDTCxLQUFLLEVBQ0wsUUFBUSxFQUNSLFNBQVMsRUFDVCxTQUFTLEVBQ1QsVUFBVSxHQUNYLENBQUMifQ==
package/license.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Task Venture Capital GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/npmextra.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "@git.zone/cli": {
3
+ "projectType": "npm",
4
+ "module": {
5
+ "githost": "code.foss.global",
6
+ "gitscope": "git.zone",
7
+ "gitrepo": "tsrust",
8
+ "description": "A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.",
9
+ "npmPackagename": "@git.zone/tsrust",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "Rust",
13
+ "cargo",
14
+ "compilation",
15
+ "CLI tool",
16
+ "build tool",
17
+ "workspace",
18
+ "binary"
19
+ ]
20
+ },
21
+ "release": {
22
+ "registries": [
23
+ "https://verdaccio.lossless.digital",
24
+ "https://registry.npmjs.org"
25
+ ],
26
+ "accessLevel": "public"
27
+ }
28
+ }
29
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@git.zone/tsrust",
3
+ "version": "1.0.2",
4
+ "private": false,
5
+ "description": "A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.",
6
+ "main": "dist_ts/index.js",
7
+ "typings": "dist_ts/index.d.ts",
8
+ "type": "module",
9
+ "bin": {
10
+ "tsrust": "./cli.js"
11
+ },
12
+ "scripts": {
13
+ "test": "tstest test/test.ts --verbose",
14
+ "build": "tsbuild --web --skiplibcheck"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://code.foss.global/git.zone/tsrust.git"
19
+ },
20
+ "keywords": [
21
+ "Rust",
22
+ "cargo",
23
+ "compilation",
24
+ "CLI tool",
25
+ "build tool",
26
+ "workspace",
27
+ "binary"
28
+ ],
29
+ "author": "Task Venture Capital GmbH",
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://code.foss.global/git.zone/tsrust/issues"
33
+ },
34
+ "homepage": "https://code.foss.global/git.zone/tsrust#README",
35
+ "dependencies": {
36
+ "@push.rocks/early": "^4.0.4",
37
+ "@push.rocks/smartcli": "^4.0.19",
38
+ "@push.rocks/smartfile": "^13.1.2",
39
+ "@push.rocks/smartpath": "^6.0.0",
40
+ "@push.rocks/smartshell": "^3.0.6",
41
+ "smol-toml": "^1.3.1"
42
+ },
43
+ "devDependencies": {
44
+ "@git.zone/tsbuild": "^4.1.2",
45
+ "@git.zone/tsrun": "^2.0.1",
46
+ "@git.zone/tstest": "^3.1.4",
47
+ "@types/node": "^22.15.0"
48
+ },
49
+ "files": [
50
+ "ts/**/*",
51
+ "dist/**/*",
52
+ "dist_*/**/*",
53
+ "dist_ts/**/*",
54
+ "assets/**/*",
55
+ "cli.js",
56
+ "npmextra.json",
57
+ "readme.md"
58
+ ],
59
+ "browserslist": [
60
+ "last 1 chrome versions"
61
+ ]
62
+ }
@@ -0,0 +1,12 @@
1
+ # tsrust hints
2
+
3
+ ## Architecture
4
+ - Follows tsbuild patterns exactly (cli.js, cli.child.ts, cli.ts.js entry points)
5
+ - Uses smartcli for CLI, smartshell for cargo execution, smol-toml for TOML parsing
6
+ - Three modules: mod_cli, mod_cargo, mod_fs
7
+
8
+ ## Key patterns
9
+ - smartshell `.exec()` streams output to terminal (non-silent)
10
+ - smartshell `.execSilent()` captures output without printing
11
+ - Cargo workspace detection: check for `[workspace]` section in Cargo.toml
12
+ - Binary targets: look for `[[bin]]` entries in member crate Cargo.toml files
package/readme.md ADDED
@@ -0,0 +1,179 @@
1
+ # @git.zone/tsrust
2
+
3
+ A CLI build tool for Rust projects that follows the same conventions as `@git.zone/tsbuild`. It detects your `rust/` source directory, parses `Cargo.toml` (including workspaces), runs `cargo build --release`, and copies the resulting binaries into a clean `dist_rust/` directory at the project root.
4
+
5
+ ## Issue Reporting and Security
6
+
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
8
+
9
+ ## Install
10
+
11
+ Install globally via npm:
12
+
13
+ ```bash
14
+ npm install -g @git.zone/tsrust
15
+ ```
16
+
17
+ Or as a project-level dev dependency:
18
+
19
+ ```bash
20
+ pnpm install --save-dev @git.zone/tsrust
21
+ ```
22
+
23
+ > ⚡ **Prerequisite:** You need a working Rust toolchain. Install via [rustup.rs](https://rustup.rs/) if you haven't already.
24
+
25
+ ## The Convention
26
+
27
+ `tsrust` mirrors the directory convention established by `tsbuild`:
28
+
29
+ | Tool | Source Directory | Output Directory |
30
+ |------|-----------------|-----------------|
31
+ | `tsbuild` | `ts/` | `dist_ts/` |
32
+ | **`tsrust`** | **`rust/`** | **`dist_rust/`** |
33
+
34
+ Your Rust code lives in `rust/` (or `ts_rust/` as fallback), and compiled binaries land in `dist_rust/` — ready for packaging, deployment, or further tooling.
35
+
36
+ ## Usage
37
+
38
+ ### 🔨 Build (Default Command)
39
+
40
+ Simply run `tsrust` from your project root:
41
+
42
+ ```bash
43
+ tsrust
44
+ ```
45
+
46
+ This will:
47
+ 1. Verify that `cargo` is available
48
+ 2. Locate your `rust/` directory (containing `Cargo.toml`)
49
+ 3. Parse the workspace to discover all `[[bin]]` targets
50
+ 4. Run `cargo build --release` with full streaming output
51
+ 5. Copy each binary to `dist_rust/` with executable permissions (`chmod 755`)
52
+ 6. Report file sizes and total build time
53
+
54
+ **Example output:**
55
+
56
+ ```
57
+ Using cargo 1.90.0 (840b83a10 2025-07-30)
58
+ Found Rust project at: rust
59
+ Detected Cargo workspace
60
+ Binary targets: rustproxy
61
+ Running: cargo build --release
62
+ Compiling rustproxy v0.1.0
63
+ Finished `release` profile [optimized] target(s) in 29.01s
64
+ Copied rustproxy (13.4 MB) -> dist_rust/rustproxy
65
+ Done in 29.2s
66
+ ```
67
+
68
+ ### 🐛 Debug Build
69
+
70
+ Build with the debug profile instead of release:
71
+
72
+ ```bash
73
+ tsrust --debug
74
+ ```
75
+
76
+ Binaries are taken from `rust/target/debug/` instead of `rust/target/release/`.
77
+
78
+ ### 🧹 Clean Before Building
79
+
80
+ Run `cargo clean` before building to force a full rebuild:
81
+
82
+ ```bash
83
+ tsrust --clean
84
+ ```
85
+
86
+ ### 🗑️ Clean Only
87
+
88
+ Remove all build artifacts without rebuilding:
89
+
90
+ ```bash
91
+ tsrust clean
92
+ ```
93
+
94
+ This runs `cargo clean` in the Rust directory and deletes the `dist_rust/` output directory.
95
+
96
+ ## Project Structure
97
+
98
+ `tsrust` expects your project to follow this layout:
99
+
100
+ ```
101
+ my-project/
102
+ ├── rust/ # 🦀 Your Rust source code
103
+ │ ├── Cargo.toml # Root manifest (workspace or single crate)
104
+ │ ├── src/
105
+ │ │ └── main.rs # (for single-crate projects)
106
+ │ └── crates/ # (for workspace projects)
107
+ │ ├── my-binary/
108
+ │ │ ├── Cargo.toml # Contains [[bin]] targets
109
+ │ │ └── src/
110
+ │ └── my-lib/
111
+ │ ├── Cargo.toml
112
+ │ └── src/
113
+ ├── dist_rust/ # 📦 Output: compiled binaries go here
114
+ │ └── my-binary
115
+ ├── ts/ # (your TypeScript code, built by tsbuild)
116
+ ├── dist_ts/ # (TypeScript output)
117
+ └── package.json
118
+ ```
119
+
120
+ ### Workspace Support
121
+
122
+ `tsrust` fully supports Cargo workspaces. It reads the `[workspace]` section from your root `Cargo.toml`, iterates through all `members`, and discovers binary targets from each member crate's `Cargo.toml`.
123
+
124
+ Binary target discovery follows Cargo's own rules:
125
+ - **Explicit `[[bin]]` entries** → uses the `name` field from each entry
126
+ - **Implicit binary** → if no `[[bin]]` is declared but `src/main.rs` exists, uses the `[package] name`
127
+ - **Library-only crates** → skipped (no binary output expected)
128
+
129
+ ### Fallback Directory
130
+
131
+ If no `rust/` directory is found, `tsrust` checks for `ts_rust/` as a fallback. This supports projects that use the `ts_` prefix convention for all source directories.
132
+
133
+ ## Programmatic API
134
+
135
+ `tsrust` exports its internals for use in other Node.js/TypeScript tools:
136
+
137
+ ```typescript
138
+ import { CargoConfig, CargoRunner, FsHelpers, TsRustCli } from '@git.zone/tsrust';
139
+
140
+ // Parse a Cargo workspace
141
+ const config = new CargoConfig('/path/to/rust');
142
+ const info = await config.parse();
143
+ console.log(info.isWorkspace); // true
144
+ console.log(info.binTargets); // ['rustproxy']
145
+
146
+ // Run cargo build
147
+ const runner = new CargoRunner('/path/to/rust');
148
+ const result = await runner.build({ debug: false, clean: false });
149
+ console.log(result.success); // true
150
+ console.log(result.exitCode); // 0
151
+
152
+ // File helpers
153
+ await FsHelpers.ensureEmptyDir('/path/to/dist_rust');
154
+ await FsHelpers.copyFile(src, dest);
155
+ await FsHelpers.makeExecutable(dest);
156
+ const size = await FsHelpers.getFileSize(dest);
157
+ console.log(FsHelpers.formatFileSize(size)); // "13.4 MB"
158
+ ```
159
+
160
+ ## License and Legal Information
161
+
162
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
163
+
164
+ **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
165
+
166
+ ### Trademarks
167
+
168
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
169
+
170
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
171
+
172
+ ### Company Information
173
+
174
+ Task Venture Capital GmbH
175
+ Registered at District Court Bremen HRB 35230 HB, Germany
176
+
177
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
178
+
179
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@git.zone/tsrust',
6
+ version: '1.0.2',
7
+ description: 'A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.'
8
+ }
package/ts/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import * as plugins from './plugins.js';
2
+ plugins.early.start('@git.zone/tsrust');
3
+
4
+ export * from './mod_fs/index.js';
5
+ export * from './mod_cargo/index.js';
6
+ export * from './mod_cli/index.js';
7
+
8
+ plugins.early.stop();
@@ -0,0 +1,89 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import * as smolToml from 'smol-toml';
4
+ import { FsHelpers } from '../mod_fs/index.js';
5
+
6
+ export interface ICargoWorkspaceInfo {
7
+ isWorkspace: boolean;
8
+ rustDir: string;
9
+ binTargets: string[];
10
+ }
11
+
12
+ export class CargoConfig {
13
+ private rustDir: string;
14
+
15
+ constructor(rustDir: string) {
16
+ this.rustDir = rustDir;
17
+ }
18
+
19
+ public async parse(): Promise<ICargoWorkspaceInfo> {
20
+ const cargoTomlPath = path.join(this.rustDir, 'Cargo.toml');
21
+ const content = await fs.promises.readFile(cargoTomlPath, 'utf-8');
22
+ const parsed = smolToml.parse(content);
23
+
24
+ const isWorkspace = !!(parsed as any).workspace;
25
+ let binTargets: string[] = [];
26
+
27
+ if (isWorkspace) {
28
+ binTargets = await this.collectWorkspaceBinTargets(parsed);
29
+ } else {
30
+ binTargets = this.collectCrateBinTargets(parsed, this.rustDir);
31
+ }
32
+
33
+ return {
34
+ isWorkspace,
35
+ rustDir: this.rustDir,
36
+ binTargets,
37
+ };
38
+ }
39
+
40
+ private async collectWorkspaceBinTargets(parsed: any): Promise<string[]> {
41
+ const members: string[] = parsed.workspace?.members || [];
42
+ const binTargets: string[] = [];
43
+
44
+ for (const member of members) {
45
+ const memberDir = path.join(this.rustDir, member);
46
+ const memberCargoToml = path.join(memberDir, 'Cargo.toml');
47
+
48
+ if (!(await FsHelpers.fileExists(memberCargoToml))) {
49
+ continue;
50
+ }
51
+
52
+ const memberContent = await fs.promises.readFile(memberCargoToml, 'utf-8');
53
+ const memberParsed = smolToml.parse(memberContent);
54
+ const memberBins = this.collectCrateBinTargets(memberParsed, memberDir);
55
+ binTargets.push(...memberBins);
56
+ }
57
+
58
+ return binTargets;
59
+ }
60
+
61
+ private collectCrateBinTargets(parsed: any, crateDir: string): string[] {
62
+ const binTargets: string[] = [];
63
+
64
+ // Check for explicit [[bin]] entries
65
+ if (Array.isArray(parsed.bin)) {
66
+ for (const bin of parsed.bin) {
67
+ if (bin.name) {
68
+ binTargets.push(bin.name);
69
+ }
70
+ }
71
+ }
72
+
73
+ // If no [[bin]] but package has a name and src/main.rs exists, use package name
74
+ if (binTargets.length === 0 && parsed.package?.name) {
75
+ const mainRsPath = path.join(crateDir, 'src', 'main.rs');
76
+ // Use sync check since this is called during parsing
77
+ try {
78
+ const stat = fs.statSync(mainRsPath);
79
+ if (stat.isFile()) {
80
+ binTargets.push(parsed.package.name);
81
+ }
82
+ } catch {
83
+ // No main.rs, not a binary crate
84
+ }
85
+ }
86
+
87
+ return binTargets;
88
+ }
89
+ }
@@ -0,0 +1,59 @@
1
+ import * as plugins from '../plugins.js';
2
+
3
+ export interface ICargoRunResult {
4
+ success: boolean;
5
+ exitCode: number;
6
+ stdout: string;
7
+ }
8
+
9
+ export class CargoRunner {
10
+ private shell: plugins.smartshell.Smartshell;
11
+ private rustDir: string;
12
+
13
+ constructor(rustDir: string) {
14
+ this.rustDir = rustDir;
15
+ this.shell = new plugins.smartshell.Smartshell({
16
+ executor: 'bash',
17
+ });
18
+ }
19
+
20
+ public async checkCargoInstalled(): Promise<boolean> {
21
+ const result = await this.shell.execSilent('cargo --version');
22
+ return result.exitCode === 0;
23
+ }
24
+
25
+ public async getCargoVersion(): Promise<string> {
26
+ const result = await this.shell.execSilent('cargo --version');
27
+ return result.stdout.trim();
28
+ }
29
+
30
+ public async build(options: { debug?: boolean; clean?: boolean } = {}): Promise<ICargoRunResult> {
31
+ if (options.clean) {
32
+ console.log('Cleaning previous build...');
33
+ await this.clean();
34
+ }
35
+
36
+ const profile = options.debug ? '' : ' --release';
37
+ const command = `cd ${this.rustDir} && cargo build${profile}`;
38
+
39
+ console.log(`Running: cargo build${profile}`);
40
+ const result = await this.shell.exec(command);
41
+
42
+ return {
43
+ success: result.exitCode === 0,
44
+ exitCode: result.exitCode,
45
+ stdout: result.stdout,
46
+ };
47
+ }
48
+
49
+ public async clean(): Promise<ICargoRunResult> {
50
+ const command = `cd ${this.rustDir} && cargo clean`;
51
+ const result = await this.shell.exec(command);
52
+
53
+ return {
54
+ success: result.exitCode === 0,
55
+ exitCode: result.exitCode,
56
+ stdout: result.stdout,
57
+ };
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ export { CargoConfig } from './classes.cargoconfig.js';
2
+ export { CargoRunner } from './classes.cargorunner.js';
@@ -0,0 +1,146 @@
1
+ import * as path from 'path';
2
+ import * as plugins from '../plugins.js';
3
+ import { CargoConfig } from '../mod_cargo/index.js';
4
+ import { CargoRunner } from '../mod_cargo/index.js';
5
+ import { FsHelpers } from '../mod_fs/index.js';
6
+
7
+ export class TsRustCli {
8
+ private cli: plugins.smartcli.Smartcli;
9
+ private cwd: string;
10
+
11
+ constructor(cwd: string = process.cwd()) {
12
+ this.cwd = cwd;
13
+ this.cli = new plugins.smartcli.Smartcli();
14
+ this.registerCommands();
15
+ }
16
+
17
+ private registerCommands(): void {
18
+ this.registerStandardCommand();
19
+ this.registerCleanCommand();
20
+ }
21
+
22
+ private registerStandardCommand(): void {
23
+ this.cli.standardCommand().subscribe(async (argvArg) => {
24
+ const startTime = Date.now();
25
+
26
+ // Check cargo is installed
27
+ const runner = new CargoRunner(this.cwd); // temporary, just for version check
28
+ if (!(await runner.checkCargoInstalled())) {
29
+ console.error('Error: cargo is not installed or not in PATH.');
30
+ console.error('Install Rust via https://rustup.rs/');
31
+ process.exit(1);
32
+ }
33
+
34
+ const cargoVersion = await runner.getCargoVersion();
35
+ console.log(`Using ${cargoVersion}`);
36
+
37
+ // Detect rust directory
38
+ const rustDir = await this.detectRustDir();
39
+ if (!rustDir) {
40
+ console.error('Error: No rust/ or ts_rust/ directory found with a Cargo.toml.');
41
+ process.exit(1);
42
+ }
43
+
44
+ console.log(`Found Rust project at: ${path.relative(this.cwd, rustDir) || '.'}`);
45
+
46
+ // Parse Cargo.toml
47
+ const cargoConfig = new CargoConfig(rustDir);
48
+ const workspaceInfo = await cargoConfig.parse();
49
+
50
+ if (workspaceInfo.isWorkspace) {
51
+ console.log('Detected Cargo workspace');
52
+ }
53
+
54
+ if (workspaceInfo.binTargets.length === 0) {
55
+ console.error('Error: No binary targets found in Cargo.toml.');
56
+ process.exit(1);
57
+ }
58
+
59
+ console.log(`Binary targets: ${workspaceInfo.binTargets.join(', ')}`);
60
+
61
+ // Build
62
+ const isDebug = !!(argvArg as any).debug;
63
+ const shouldClean = !!(argvArg as any).clean;
64
+ const cargoRunner = new CargoRunner(rustDir);
65
+ const buildResult = await cargoRunner.build({ debug: isDebug, clean: shouldClean });
66
+
67
+ if (!buildResult.success) {
68
+ console.error(`Build failed with exit code ${buildResult.exitCode}`);
69
+ process.exit(1);
70
+ }
71
+
72
+ // Copy binaries to dist_rust/
73
+ const profile = isDebug ? 'debug' : 'release';
74
+ const targetDir = path.join(rustDir, 'target', profile);
75
+ const distDir = path.join(this.cwd, 'dist_rust');
76
+
77
+ await FsHelpers.ensureEmptyDir(distDir);
78
+
79
+ for (const binName of workspaceInfo.binTargets) {
80
+ const srcBinary = path.join(targetDir, binName);
81
+ const destBinary = path.join(distDir, binName);
82
+
83
+ if (!(await FsHelpers.fileExists(srcBinary))) {
84
+ console.warn(`Warning: Expected binary not found: ${srcBinary}`);
85
+ continue;
86
+ }
87
+
88
+ await FsHelpers.copyFile(srcBinary, destBinary);
89
+ await FsHelpers.makeExecutable(destBinary);
90
+
91
+ const size = await FsHelpers.getFileSize(destBinary);
92
+ console.log(`Copied ${binName} (${FsHelpers.formatFileSize(size)}) -> dist_rust/${binName}`);
93
+ }
94
+
95
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
96
+ console.log(`Done in ${elapsed}s`);
97
+ });
98
+ }
99
+
100
+ private registerCleanCommand(): void {
101
+ this.cli.addCommand('clean').subscribe(async (_argvArg) => {
102
+ // Clean cargo build
103
+ const rustDir = await this.detectRustDir();
104
+ if (rustDir) {
105
+ console.log('Running cargo clean...');
106
+ const runner = new CargoRunner(rustDir);
107
+ await runner.clean();
108
+ console.log('Cargo clean complete.');
109
+ }
110
+
111
+ // Remove dist_rust/
112
+ const distDir = path.join(this.cwd, 'dist_rust');
113
+ if (await FsHelpers.directoryExists(distDir)) {
114
+ await FsHelpers.removeDirectory(distDir);
115
+ console.log('Removed dist_rust/');
116
+ }
117
+
118
+ console.log('Clean complete.');
119
+ });
120
+ }
121
+
122
+ private async detectRustDir(): Promise<string | null> {
123
+ // Check rust/ first
124
+ const rustDir = path.join(this.cwd, 'rust');
125
+ if (await FsHelpers.fileExists(path.join(rustDir, 'Cargo.toml'))) {
126
+ return rustDir;
127
+ }
128
+
129
+ // Fallback to ts_rust/
130
+ const tsRustDir = path.join(this.cwd, 'ts_rust');
131
+ if (await FsHelpers.fileExists(path.join(tsRustDir, 'Cargo.toml'))) {
132
+ return tsRustDir;
133
+ }
134
+
135
+ return null;
136
+ }
137
+
138
+ public run(): void {
139
+ this.cli.startParse();
140
+ }
141
+ }
142
+
143
+ export const runCli = async (): Promise<void> => {
144
+ const cli = new TsRustCli();
145
+ cli.run();
146
+ };
@@ -0,0 +1 @@
1
+ export { TsRustCli, runCli } from './classes.tsrustcli.js';
@@ -0,0 +1,56 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ export class FsHelpers {
5
+ public static async fileExists(filePath: string): Promise<boolean> {
6
+ try {
7
+ const stat = await fs.promises.stat(filePath);
8
+ return stat.isFile();
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+
14
+ public static async directoryExists(dirPath: string): Promise<boolean> {
15
+ try {
16
+ const stat = await fs.promises.stat(dirPath);
17
+ return stat.isDirectory();
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+
23
+ public static async ensureEmptyDir(dirPath: string): Promise<void> {
24
+ if (await FsHelpers.directoryExists(dirPath)) {
25
+ await fs.promises.rm(dirPath, { recursive: true, force: true });
26
+ }
27
+ await fs.promises.mkdir(dirPath, { recursive: true });
28
+ }
29
+
30
+ public static async copyFile(src: string, dest: string): Promise<void> {
31
+ const destDir = path.dirname(dest);
32
+ await fs.promises.mkdir(destDir, { recursive: true });
33
+ await fs.promises.copyFile(src, dest);
34
+ }
35
+
36
+ public static async makeExecutable(filePath: string): Promise<void> {
37
+ await fs.promises.chmod(filePath, 0o755);
38
+ }
39
+
40
+ public static async getFileSize(filePath: string): Promise<number> {
41
+ const stat = await fs.promises.stat(filePath);
42
+ return stat.size;
43
+ }
44
+
45
+ public static async removeDirectory(dirPath: string): Promise<void> {
46
+ if (await FsHelpers.directoryExists(dirPath)) {
47
+ await fs.promises.rm(dirPath, { recursive: true, force: true });
48
+ }
49
+ }
50
+
51
+ public static formatFileSize(bytes: number): string {
52
+ if (bytes < 1024) return `${bytes} B`;
53
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
54
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
55
+ }
56
+ }
@@ -0,0 +1 @@
1
+ export { FsHelpers } from './classes.fshelpers.js';
package/ts/plugins.ts ADDED
@@ -0,0 +1,13 @@
1
+ import * as early from '@push.rocks/early';
2
+ import * as smartcli from '@push.rocks/smartcli';
3
+ import * as smartfile from '@push.rocks/smartfile';
4
+ import * as smartpath from '@push.rocks/smartpath';
5
+ import * as smartshell from '@push.rocks/smartshell';
6
+
7
+ export {
8
+ early,
9
+ smartcli,
10
+ smartfile,
11
+ smartpath,
12
+ smartshell,
13
+ };