Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
d997246
Add Rust hosting package and VS Code debugger wiring
alexanderc-tray Jul 27, 2026
7c2d313
More fixes
afscrome Jul 28, 2026
8ee0844
Expose debug adapters as capabilities
afscrome Jul 28, 2026
ff2631e
Hosting Api tweaks
afscrome Jul 28, 2026
ed8fc76
Tweak rust app
afscrome Jul 28, 2026
f7396d9
Fix Rust publish flow and add test coverage
afscrome Jul 28, 2026
137b7ba
Merge remote-tracking branch 'origin/main' into alexanderc-trayport-r…
afscrome Jul 29, 2026
64ec642
Fix cargo arguments leaking into debugged Rust binary
afscrome Jul 29, 2026
81461c7
Clarify debug arg ordering comment and cover late cargo args
afscrome Jul 29, 2026
66e7b5a
Simplify debug arg ordering comment and link tracking issue
afscrome Jul 29, 2026
ba3f096
Shorten debug arg ordering comment
afscrome Jul 29, 2026
a1e8a35
Drop bacon and publish support; address review feedback
afscrome Jul 30, 2026
35066c3
Address remaining Rust review feedback
afscrome Jul 30, 2026
9f2e0d2
Restore publish/deploy support for Rust apps
afscrome Jul 30, 2026
5b2fc4e
Address review feedback on Rust publish support
afscrome Jul 30, 2026
1a3040e
Resolve the debug executable on the host instead of parsing cargo bui…
afscrome Jul 30, 2026
454dea8
Address review feedback: trim validation, use ExecutionContext, DI me…
afscrome Jul 30, 2026
a974caa
Rebase the cargo manifest path into the container when publishing
afscrome Jul 30, 2026
56ec8fb
Add --locked support and make the publish build defaults opt-out
afscrome Jul 30, 2026
919cbd0
Drop stale blocking-wait comment
afscrome Jul 30, 2026
949c0e0
Emit a default .dockerignore for published Rust images
afscrome Jul 31, 2026
cdb72d0
Document cargo option handling and harden the runtime user creation
afscrome Jul 31, 2026
3f6e5d4
Address review feedback on cargo target resolution and image defaults
afscrome Aug 1, 2026
377fbb9
Regenerate the extension localization bundle
afscrome Aug 1, 2026
1b4e690
Rename the Alpine check and fix .dockerignore precedence
afscrome Aug 1, 2026
44e1e05
Address Copilot review feedback on the Rust integration
afscrome Aug 2, 2026
4d28f01
Fix the dated nightly image tag order
afscrome Aug 2, 2026
edd9046
Spell out that raw cargo arguments are not validated
afscrome Aug 2, 2026
90007b1
Merge upstream/main and make the Rust debug launch configuration asyn…
afscrome Aug 5, 2026
f3e652f
Stop installing a C toolchain into the build stage
afscrome Aug 5, 2026
eb0f752
Correct the comment on stripping a channel host suffix
afscrome Aug 5, 2026
badbdf0
Stop inferring the runtime distro from the image name
afscrome Aug 5, 2026
4bd6ca1
Say musl and glibc where that is what is meant
afscrome Aug 5, 2026
75f5915
Install nothing into the runtime image either
afscrome Aug 5, 2026
7fca034
Move the publish cargo defaults next to the other cargo options
afscrome Aug 5, 2026
46bb345
Resolve the lock file from the resource working directory
afscrome Aug 5, 2026
4228217
Trim the publishing docs down to what a consumer needs
afscrome Aug 5, 2026
c434e92
Reduce the libc pairing note to a single sentence
afscrome Aug 5, 2026
a18b16d
Simplify readme
afscrome Aug 5, 2026
c8c49c3
Tidy the readme samples and warn off raw target selection
afscrome Aug 5, 2026
7d913e4
Let the runtime image allocate the app user ids
afscrome Aug 6, 2026
5ee1d61
Let rustup pick the toolchain instead of picking an image for it
afscrome Aug 6, 2026
8ebde55
Inline the base image defaults and move the runtime image to alpine 3.24
afscrome Aug 6, 2026
cc22367
Trim the Dockerfile generator comments back to what is not obvious
afscrome Aug 6, 2026
309134c
Cut the generator comments back further
afscrome Aug 6, 2026
e52415b
Read the working directory from the resource when publishing
afscrome Aug 6, 2026
9d56e03
Cache the cargo target directory and fix the cross target install
afscrome Aug 6, 2026
f6d3e43
Handle a taskkill that starts but fails
afscrome Aug 6, 2026
7a69b88
Stop the readme promising a reference it does not show
afscrome Aug 6, 2026
98a9743
Fail the Rust build when the artifact is ambiguous, drop the workspac…
afscrome Aug 6, 2026
36a5310
Document launch configuration callback context design
Aug 5, 2026
fc48eb7
docs: add launch configuration callback plan
Aug 6, 2026
ad326ea
Add launch configuration callback context
Aug 6, 2026
bfb9027
Fix launch producer restart handling
Aug 6, 2026
9a8c99a
Reset debug execution before rebuilding arguments
Aug 6, 2026
e9ea853
Migrate debug launch configuration producers
Aug 6, 2026
9efdff8
Finalize debug callback context API
Aug 6, 2026
65a56a7
Migrate debug support playground
Aug 6, 2026
ac32975
Fix debug support documentation whitespace
Aug 6, 2026
b0fcfd3
Address callback context review findings
Aug 6, 2026
8de33ce
Remove agent planning artifacts and restore load-bearing comments
Aug 7, 2026
900a331
Fix MAUI launch configuration regression and rename PrepareObjects
Aug 7, 2026
7d4d8fe
Address launch configuration context review
Aug 8, 2026
0ee07ef
Install Azure Functions Core Tools from release archive
Aug 8, 2026
2db433e
Recompute debug argument callbacks after original snapshot
Aug 8, 2026
6cb79d3
Validate legacy debug producer return type
Aug 8, 2026
1481f78
Revert unrelated Azure Functions tool install change
Aug 8, 2026
dcf4b09
Preserve launch override process args
Aug 8, 2026
024577a
Fix debug executable configuration snapshots
adamint Aug 9, 2026
24588ab
Reuse original argument resolutions across the debug rewrite
adamint Aug 9, 2026
5b8d2f3
Address review findings on debug launch configuration
adamint Aug 9, 2026
92901b8
Fix debug argument rewrite reuse and legacy overload validation
adamint Aug 10, 2026
64dd50d
Address debug launch context review feedback
Aug 10, 2026
3b95b28
Merge branch 'main' into adamint/issue-18956-launch-config-context
adamint Aug 10, 2026
a904ac7
Prevent later arg mutations from stripping debug-injected arguments
Aug 10, 2026
30d272c
Narrow debug launch context to resolved environment
Aug 10, 2026
35bea19
Clarify process-mode launch producer behavior
Aug 11, 2026
fa5c96c
Fix launch-override producer wiring
Aug 11, 2026
c5855b8
Clarify asynchronous debug producer guidance
Aug 11, 2026
7cdb91a
Cover the no-fallback rule when a launch configuration producer throws
Aug 11, 2026
b739005
Merge branch 'main' into adamint/issue-18956-launch-config-context
Aug 12, 2026
629c3ea
Merge remote-tracking branch 'upstream/main' into adamint/issue-18956…
Aug 13, 2026
92946d6
Merge PR 19077 launch configuration changes
Aug 13, 2026
45bd16e
Address Rust hosting review feedback
Aug 13, 2026
90ef108
Harden Rust publish and debugger diagnostics
Aug 13, 2026
0237479
Make Rust build cache IDs deterministic
Aug 13, 2026
7007266
Merge latest main into Rust hosting PR
Aug 13, 2026
0914793
Preserve project launch configuration compatibility
Aug 13, 2026
557eea1
Address Rust hosting review findings
Aug 13, 2026
99b5b0a
Harden Rust publish validation and lifecycle
Aug 13, 2026
3e8ea39
Validate Rust artifact path components
Aug 13, 2026
b4e8cc0
Harden Rust debugger process handling
Aug 13, 2026
f8925c0
Complete Rust debugger target detection
Aug 13, 2026
1810624
Bound Rust toolchain detection
Aug 13, 2026
7b19fea
Safely terminate Rust toolchain probes
Aug 13, 2026
2ec7766
Cover Rust probe graceful shutdown
Aug 13, 2026
ed225d4
Update Rust debugger localization catalog
Aug 13, 2026
24e5a9c
Merge latest main before Rust PR validation
Aug 13, 2026
66b3536
Fix Windows Rust CI expectations
Aug 13, 2026
05cc077
Redact credential-bearing URL values
Aug 13, 2026
1a17c23
Reject Cargo credentials in generated Dockerfiles
Aug 13, 2026
ac060df
Harden Rust artifact diagnostics
Aug 13, 2026
387d216
Update Windows Rust publish snapshot
Aug 13, 2026
2b14a7a
Restore launch configuration inspection API
Aug 13, 2026
98f57a6
Merge main into Rust hosting branch
Aug 13, 2026
3564e99
Update Aspire schemas and Rust playground launch configuration
Aug 13, 2026
e7a88f7
Add repo-local Aspire CLI runner
Aug 13, 2026
ea33451
Remove retired Aspire Skills schema flag
Aug 13, 2026
bf2a5ec
Add Rust AppHost debugging support
Aug 14, 2026
b3862db
Document Rust AppHost parity design
Aug 14, 2026
ddde2bb
Plan Rust AppHost parity implementation
Aug 14, 2026
0df061a
Add Rust AppHost editor parser
Aug 14, 2026
684f2bb
Redact Cargo metadata diagnostics
Aug 14, 2026
9e76d11
Test Rust AppHost editor features
Aug 14, 2026
d82a8da
Align Rust AppHost scaffold and runtime
Aug 14, 2026
385b1ed
Classify Rust AppHosts in telemetry
Aug 14, 2026
3c5d0c8
Document Rust AppHost parity work
Aug 14, 2026
1dd3259
Harden Rust AppHost parsing
Aug 14, 2026
7a1ec85
Recognize Rust Cargo AppHosts in telemetry
Aug 14, 2026
d124f6b
Redact Cargo metadata diagnostics
Aug 14, 2026
81368c5
Improve resource command failure notifications
Aug 14, 2026
4eaabf1
Guide Rust AppHost runs through Aspire
Aug 14, 2026
32eecc9
Gate Rust ATS diagnostics behind ASPIRE_DEBUG
Aug 14, 2026
5d0f18f
Suppress generated Rust SDK warnings
Aug 14, 2026
26e7437
Disable incremental Rust AppHost debug builds
Aug 14, 2026
588e217
Accumulate Rust cargo features
Aug 14, 2026
6d2d977
Fix Rust SDK validation scaffold path
Aug 14, 2026
3b7c429
Remove Rust planning documents
Aug 14, 2026
08db695
Remove unused duplicate isBuildableAppHostCandidate from utils/workspace
Aug 14, 2026
c3133c8
Clarify Rust OTLP certificate setup
Aug 14, 2026
5334688
Consolidate path comparison, shortening, and filesystem identity help…
Aug 14, 2026
a902715
Deduplicate delay, stripResourceSuffix, and getErrorMessage helpers
Aug 14, 2026
5de74ad
Share tree-sitter AST helpers between the C# and Rust AppHost parsers
Aug 14, 2026
79f0b0f
Move AppHostDataRepository from views/ to data/ to match its layer
Aug 14, 2026
8ff2955
Extract CLI wire contracts and one-shot CLI runner from AppHostDataRe…
Aug 14, 2026
eb3750f
Give AppHostCliRunner a conventional public API and Disposable teardown
Aug 14, 2026
9f1ba41
Extract ps poller, describe stream manager, and status-output filteri…
Aug 14, 2026
ae1a73d
Move app-host path matching predicates out of the data layer
Aug 14, 2026
5b87a30
Split tree item classes and presentation helpers out of AspireAppHost…
Aug 14, 2026
a532518
Extract AppHostParentOutputFilter into its own module
Aug 14, 2026
3d37600
Move generic CLI process spawn/terminate helpers into utils/process
Aug 14, 2026
9ab057f
Make AppHostPsPoller.dispose release its timer and child processes
Aug 14, 2026
d748c85
Point imports at real modules and drop dead re-export shims
Aug 14, 2026
5e4a8c9
Split extension activation command registration into focused modules
Aug 14, 2026
9ff2fbb
Group app-host discovery helper functions into cohesive modules
Aug 14, 2026
49ce3d1
Remove unused InteractionService import from extension activation
Aug 14, 2026
07f7213
Support hierarchical local package feeds
Aug 14, 2026
10acf80
Extract app-host launch contracts, reservation bookkeeping, and telem…
Aug 14, 2026
037eecb
Extract dashboard and browser launch handling from AspireDebugSession
Aug 14, 2026
1c3c231
Separate app-host lifecycle tool contracts and language model adapter…
Aug 14, 2026
7d698c8
Address extension review threads: cross-platform log paths, Rust debu…
Aug 14, 2026
694e742
Address Rust hosting review threads: cache scoping, URL redaction, lo…
Aug 14, 2026
f9be1da
Merge remote-tracking branch 'microsoft/main' into rust-hosting-worktree
Aug 14, 2026
17fea22
Honor caller-supplied mappings for local template discovery and make …
Aug 14, 2026
3e50ad2
Stop leaking one-shot CLI processes and tighten describe compatibilit…
Aug 14, 2026
268a0ea
Fix missing Rust loc catalog entry, unlisted CodeLens command, and tu…
Aug 14, 2026
453da9c
Decouple Aspire.Hosting.Rust from Hosting internals and give cargo ar…
Aug 14, 2026
5ef1dcc
Make the Rust playground AppHost launchable and drop its unused deps
Aug 14, 2026
1258dda
Align package-surface and source-override E2E assertions with shipped…
Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Aspire-Core.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"src\\Aspire.Hosting.JavaScript\\Aspire.Hosting.JavaScript.csproj",
"src\\Aspire.Hosting.PostgreSQL\\Aspire.Hosting.PostgreSQL.csproj",
"src\\Aspire.Hosting.Python\\Aspire.Hosting.Python.csproj",
"src\\Aspire.Hosting.Rust\\Aspire.Hosting.Rust.csproj",
"src\\Aspire.Hosting.RabbitMQ\\Aspire.Hosting.RabbitMQ.csproj",
"src\\Aspire.Hosting.Redis\\Aspire.Hosting.Redis.csproj",
"src\\Aspire.Hosting.SqlServer\\Aspire.Hosting.SqlServer.csproj",
Expand All @@ -58,6 +59,7 @@
"tests\\Aspire.Hosting.EntityFrameworkCore.Tests\\Aspire.Hosting.EntityFrameworkCore.Tests.csproj",
"tests\\Aspire.Hosting.Testing.Tests\\Aspire.Hosting.Testing.Tests.csproj",
"tests\\Aspire.Hosting.Tests\\Aspire.Hosting.Tests.csproj",
"tests\\Aspire.Hosting.Rust.Tests\\Aspire.Hosting.Rust.Tests.csproj",
"tests\\Aspire.TestUtilities\\Aspire.TestUtilities.csproj",
"tests\\TestingAppHost1\\TestingAppHost1.AppHost\\TestingAppHost1.AppHost.csproj",
"tests\\TestingAppHost1\\TestingAppHost1.MyWebApp\\TestingAppHost1.MyWebApp.csproj",
Expand Down
2 changes: 2 additions & 0 deletions Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<Project Path="src/Aspire.Hosting.Orleans/Aspire.Hosting.Orleans.csproj" />
<Project Path="src/Aspire.Hosting.PostgreSQL/Aspire.Hosting.PostgreSQL.csproj" />
<Project Path="src/Aspire.Hosting.Python/Aspire.Hosting.Python.csproj" />
<Project Path="src/Aspire.Hosting.Rust/Aspire.Hosting.Rust.csproj" />
<Project Path="src/Aspire.Hosting.Qdrant/Aspire.Hosting.Qdrant.csproj" />
<Project Path="src/Aspire.Hosting.RabbitMQ/Aspire.Hosting.RabbitMQ.csproj" />
<Project Path="src/Aspire.Hosting.Radius/Aspire.Hosting.Radius.csproj" />
Expand Down Expand Up @@ -547,6 +548,7 @@
<Project Path="tests/Aspire.Hosting.Orleans.Tests/Aspire.Hosting.Orleans.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.PostgreSQL.Tests/Aspire.Hosting.PostgreSQL.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.Python.Tests/Aspire.Hosting.Python.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.Rust.Tests/Aspire.Hosting.Rust.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.Qdrant.Tests/Aspire.Hosting.Qdrant.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.RabbitMQ.Tests/Aspire.Hosting.RabbitMQ.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.Radius.Tests/Aspire.Hosting.Radius.Tests.csproj" />
Expand Down
6 changes: 3 additions & 3 deletions extension/loc/xlf/aspire-vscode.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions extension/src/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export type Capability =
| 'ms-python.python' // Older AppHost versions used this extension identifier instead of python
| 'go' // Support for running Go projects
| 'golang.go' // Older AppHost versions used this extension identifier instead of go
| 'cppvsdbg' // C++ debugger adapter type used for Rust debugging on Windows
| 'cppdbg' // C++ debugger adapter type fallback
| 'rust' // Support for running Rust projects
| 'rust-lang.rust-analyzer' // Rust Analyzer extension identifier
| 'vadimcn.vscode-lldb' // CodeLLDB extension identifier
| 'node' // Support for running Node.js projects
| 'bun' // Support for running Bun projects
| 'oven.bun-vscode' // Bun debug adapter extension identifier
Expand All @@ -24,12 +29,42 @@ export type Capability =
| 'azure-functions'; // Support for running Azure Functions projects

export type Capabilities = Capability[];
export type RustDebugAdapterType = 'cppvsdbg' | 'cppdbg' | 'lldb' | 'codelldb';

function isExtensionInstalled(extensionId: string): boolean {
const extension = vscode.extensions.getExtension(extensionId);
return !!extension;
}

function hasDebuggerType(debuggerType: RustDebugAdapterType): boolean {
return vscode.extensions.all.some((extension) => {
const debuggers = (extension.packageJSON as { contributes?: { debuggers?: Array<{ type?: string }> } }).contributes?.debuggers;
return Array.isArray(debuggers) && debuggers.some(entry => entry.type === debuggerType);
});
}

export function getRustDebugAdapterType(): RustDebugAdapterType | undefined {
if (process.platform === 'win32') {
if (hasDebuggerType('cppvsdbg')) {
return 'cppvsdbg';
}

if (hasDebuggerType('cppdbg')) {
return 'cppdbg';
}
}

if (hasDebuggerType('lldb') || isExtensionInstalled("vadimcn.vscode-lldb")) {
return 'lldb';
}

if (hasDebuggerType('codelldb')) {
return 'codelldb';
}

return undefined;
}

export function isCsDevKitInstalled() {
return isExtensionInstalled("ms-dotnettools.csdevkit");
}
Expand All @@ -46,6 +81,10 @@ export function isGoInstalled() {
return isExtensionInstalled("golang.go");
}

export function isRustInstalled() {
return getRustDebugAdapterType() !== undefined;
}

export function isAzureFunctionsExtensionInstalled() {
return isExtensionInstalled("ms-azuretools.vscode-azurefunctions");
}
Expand Down Expand Up @@ -92,6 +131,18 @@ export function getSupportedCapabilities(): Capabilities {
capabilities.push("golang.go");
}

if (isRustInstalled()) {
capabilities.push("rust");

if (isExtensionInstalled("rust-lang.rust-analyzer")) {
capabilities.push("rust-lang.rust-analyzer");
}

if (isExtensionInstalled("vadimcn.vscode-lldb")) {
capabilities.push("vadimcn.vscode-lldb");
}
}

if (isNodeInstalled()) {
capabilities.push("node");
capabilities.push("browser");
Expand Down
15 changes: 15 additions & 0 deletions extension/src/dcp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ export function isGoLaunchConfiguration(obj: any): obj is GoLaunchConfiguration
return obj && obj.type === 'go';
}

export interface RustCargoLaunchTarget {
args?: string[];
filter?: string;
}

export interface RustLaunchConfiguration extends ExecutableLaunchConfiguration {
type: "rust";
cargo?: RustCargoLaunchTarget;
working_directory?: string;
}

export function isRustLaunchConfiguration(obj: any): obj is RustLaunchConfiguration {
return obj && obj.type === 'rust';
}

export interface JavaScriptRuntimeLaunchConfiguration extends ExecutableLaunchConfiguration {
type: "node" | "bun";
script_path?: string;
Expand Down
7 changes: 6 additions & 1 deletion extension/src/debugger/debuggerExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { debugProject, runProject } from "../loc/strings";
import { getEnvironmentWithoutE2EBridgeVariables, mergeEnvs } from "../utils/environment";
import { extensionLogOutputChannel } from "../utils/logging";
import { projectDebuggerExtension } from "./languages/dotnet";
import { isAzureFunctionsExtensionInstalled, isBunInstalled, isCsharpInstalled, isGoInstalled, isMauiInstalled, isPythonInstalled } from '../capabilities';
import { isAzureFunctionsExtensionInstalled, isBunInstalled, isCsharpInstalled, isGoInstalled, isMauiInstalled, isPythonInstalled, isRustInstalled } from '../capabilities';
import { pythonDebuggerExtension } from "./languages/python";
import { nodeDebuggerExtension } from "./languages/node";
import { browserDebuggerExtension } from "./languages/browser";
import { azureFunctionsDebuggerExtension } from "./languages/azureFunctions";
import { goDebuggerExtension } from "./languages/go";
import { rustDebuggerExtension } from "./languages/rust";
import { bunDebuggerExtension } from "./languages/bun";
import { mauiDebuggerExtension } from "./languages/maui";
import { isDirectory } from "../utils/io";
Expand Down Expand Up @@ -89,6 +90,10 @@ export function getResourceDebuggerExtensions(): ResourceDebuggerExtension[] {
extensions.push(goDebuggerExtension);
}

if (isRustInstalled()) {
extensions.push(rustDebuggerExtension);
}

extensions.push(nodeDebuggerExtension);
extensions.push(browserDebuggerExtension);

Expand Down
83 changes: 83 additions & 0 deletions extension/src/debugger/languages/rust.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as path from 'path';
import * as vscode from 'vscode';
import { getRustDebugAdapterType } from "../../capabilities";
import { AspireResourceExtendedDebugConfiguration, ExecutableLaunchConfiguration, RustLaunchConfiguration, isRustLaunchConfiguration } from "../../dcp/types";
import { invalidLaunchConfiguration, rustDisplayName, rustLabel } from "../../loc/strings";
import { extensionLogOutputChannel } from "../../utils/logging";
import { ResourceDebuggerExtension } from "../debuggerExtensions";

const defaultRustDebugAdapter = getRustDebugAdapterType() ?? 'codelldb';

function getProjectFile(launchConfig: ExecutableLaunchConfiguration): string {
if (isRustLaunchConfiguration(launchConfig) && launchConfig.working_directory) {
return launchConfig.working_directory;
}

throw new Error(invalidLaunchConfiguration(JSON.stringify(launchConfig)));
}

function createCargoConfiguration(launchConfig: RustLaunchConfiguration): { args: string[]; filter?: string } | undefined {
if (!launchConfig.cargo) {
return undefined;
}

return {
args: launchConfig.cargo.args ?? [],
...(launchConfig.cargo.filter ? { filter: launchConfig.cargo.filter } : {})
};
}

function getRustBinaryPath(launchConfig: RustLaunchConfiguration): string {
const workingDirectory = launchConfig.working_directory ?? '.';
const binaryName = launchConfig.cargo?.filter ?? path.basename(workingDirectory);
const executableName = process.platform === 'win32'
? `${binaryName}.exe`
: binaryName;

return path.join(workingDirectory, 'target', 'debug', executableName);
Comment thread
afscrome marked this conversation as resolved.
Resolved
}

export const rustDebuggerExtension: ResourceDebuggerExtension = {
resourceType: 'rust',
debugAdapter: defaultRustDebugAdapter,
extensionId: null,
getDisplayName: (launchConfiguration: ExecutableLaunchConfiguration) => {
if (isRustLaunchConfiguration(launchConfiguration) && launchConfiguration.working_directory) {
return rustDisplayName(vscode.workspace.asRelativePath(launchConfiguration.working_directory));
}

return rustLabel;
},
getSupportedFileTypes: () => ['.rs'],
getProjectFile: (launchConfig) => getProjectFile(launchConfig),
createDebugSessionConfigurationCallback: async (launchConfig, args, _env, launchOptions, debugConfiguration: AspireResourceExtendedDebugConfiguration): Promise<void> => {
if (!isRustLaunchConfiguration(launchConfig)) {
extensionLogOutputChannel.info(`The resource type was not rust for ${JSON.stringify(launchConfig)}`);
throw new Error(invalidLaunchConfiguration(JSON.stringify(launchConfig)));
}

const debugAdapter = getRustDebugAdapterType();
if (debugAdapter === undefined) {
throw new Error(invalidLaunchConfiguration(JSON.stringify(launchConfig)));
}

debugConfiguration.type = debugAdapter;
debugConfiguration.request = 'launch';
debugConfiguration.noDebug = !launchOptions.debug;
debugConfiguration.cwd = launchConfig.working_directory;
debugConfiguration.args = args ?? [];

if (debugAdapter === 'cppvsdbg' || debugAdapter === 'cppdbg') {
debugConfiguration.program = getRustBinaryPath(launchConfig);
delete debugConfiguration.cargo;
return;
}

const cargo = createCargoConfiguration(launchConfig);
if (cargo) {
debugConfiguration.cargo = cargo;
}

delete debugConfiguration.program;
}
};
2 changes: 2 additions & 0 deletions extension/src/loc/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ export const browserDisplayName = (url: string) => vscode.l10n.t('Browser: {0}',
export const browserLabel = vscode.l10n.t('Browser');
export const goDisplayName = (program: string) => `Go: ${program}`;
export const goLabel = 'Go';
export const rustDisplayName = (program: string) => vscode.l10n.t('Rust: {0}', program);
export const rustLabel = vscode.l10n.t('Rust');
export const bunDisplayName = (script: string) => `Bun: ${script}`;
export const bunLabel = 'Bun';
export const nodeDisplayName = (script: string) => `Node.js: ${script}`;
Expand Down
114 changes: 114 additions & 0 deletions extension/src/test/rustDebugger.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import * as assert from 'assert';
import * as path from 'path';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import { getSupportedCapabilities } from '../capabilities';
import { AspireDebugSession } from '../debugger/AspireDebugSession';
import { getResourceDebuggerExtensions } from '../debugger/debuggerExtensions';
import { rustDebuggerExtension } from '../debugger/languages/rust';
import { AspireResourceExtendedDebugConfiguration, RustLaunchConfiguration } from '../dcp/types';

suite('Rust Debugger Extension Tests', () => {
const fakeAspireDebugSession = {} as AspireDebugSession;

teardown(() => sinon.restore());

test('advertises Rust support when a Rust debugger extension is installed', () => {
sinon.stub(vscode.extensions, 'getExtension').returns(undefined);
sinon.stub(vscode.extensions, 'all').value([{
packageJSON: {
contributes: {
debuggers: [{ type: 'codelldb' }]
}
}
} as unknown as vscode.Extension<unknown>]);

const capabilities = getSupportedCapabilities();
assert.ok(capabilities.includes('rust'));
assert.ok(getResourceDebuggerExtensions().some(extension => extension.resourceType === 'rust'));
});

test('configures VS Code Rust debugger with cargo metadata', async () => {
sinon.stub(vscode.extensions, 'getExtension').returns(undefined);
sinon.stub(vscode.extensions, 'all').value([{
packageJSON: {
contributes: {
debuggers: [{ type: 'codelldb' }]
}
}
} as unknown as vscode.Extension<unknown>]);

const launchConfig: RustLaunchConfiguration = {
type: 'rust',
working_directory: '/workspace/rust-app',
cargo: {
args: ['build', '--release'],
filter: 'rust-app'
}
};
const debugConfig = createDebugConfig();

await rustDebuggerExtension.createDebugSessionConfigurationCallback!(
launchConfig,
['--port', '8080'],
[],
{ debug: true, runId: '1', debugSessionId: '1', isApphost: false, debugSession: fakeAspireDebugSession },
debugConfig);

assert.ok(['cppvsdbg', 'cppdbg', 'codelldb', 'lldb'].includes(debugConfig.type));
assert.strictEqual(debugConfig.request, 'launch');
assert.strictEqual(debugConfig.cwd, '/workspace/rust-app');
assert.deepStrictEqual(debugConfig.args, ['--port', '8080']);
if (debugConfig.type === 'cppvsdbg' || debugConfig.type === 'cppdbg') {
assert.strictEqual(debugConfig.cargo, undefined);
const expectedExecutable = process.platform === 'win32' ? 'rust-app.exe' : 'rust-app';
assert.strictEqual(debugConfig.program, path.join('/workspace/rust-app', 'target', 'debug', expectedExecutable));
}
else {
assert.deepStrictEqual(debugConfig.cargo, { args: ['build', '--release'], filter: 'rust-app' });
assert.strictEqual(debugConfig.program, undefined);
}
assert.strictEqual(debugConfig.noDebug, false);
});

test('sets noDebug when launch option disables debugging', async () => {
sinon.stub(vscode.extensions, 'getExtension').returns(undefined);
sinon.stub(vscode.extensions, 'all').value([{
packageJSON: {
contributes: {
debuggers: [{ type: 'lldb' }]
}
}
} as unknown as vscode.Extension<unknown>]);

const launchConfig: RustLaunchConfiguration = {
type: 'rust',
working_directory: '/workspace/rust-app',
cargo: {
args: ['build']
}
};
const debugConfig = createDebugConfig();

await rustDebuggerExtension.createDebugSessionConfigurationCallback!(
launchConfig,
[],
[],
{ debug: false, runId: '1', debugSessionId: '1', isApphost: false, debugSession: fakeAspireDebugSession },
debugConfig);

assert.strictEqual(debugConfig.noDebug, true);
});
});

function createDebugConfig(): AspireResourceExtendedDebugConfiguration {
return {
runId: '1',
debugSessionId: '1',
type: 'rust',
name: 'Rust',
request: 'launch',
program: '/workspace/rust-app',
args: []
};
}
Loading
Loading