| File: RustLanguageSupport.cs | Web Access |
| Project: src\src\Aspire.Hosting.CodeGeneration.Rust\Aspire.Hosting.CodeGeneration.Rust.csproj (Aspire.Hosting.CodeGeneration.Rust) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Aspire.TypeSystem; namespace Aspire.Hosting.CodeGeneration.Rust; /// <summary> /// Provides language support for Rust AppHosts. /// Implements scaffolding, detection, and runtime configuration. /// </summary> internal sealed class RustLanguageSupport : ILanguageSupport { /// <summary> /// The language/runtime identifier for Rust. /// </summary> private const string LanguageId = "rust"; private const string AppHostFileName = "apphost.rs"; // Must stay in sync with the [[bin]] target the scaffolded Cargo.toml below declares. private const string AppHostBinaryName = "apphost"; /// <summary> /// The code generation target language. This maps to the ICodeGenerator.Language property. /// </summary> private const string CodeGenTarget = "Rust"; private const string LanguageDisplayName = "Rust"; private static readonly string[] s_detectionPatterns = [AppHostFileName]; /// <inheritdoc /> public string Language => LanguageId; /// <inheritdoc /> public Dictionary<string, string> Scaffold(ScaffoldRequest request) { var files = new Dictionary<string, string>(); files[AppHostFileName] = """ // Aspire Rust AppHost // For more information, see: https://aspire.dev #[path = ".aspire/modules/mod.rs"] mod aspire; use aspire::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let builder = create_builder(None)?; // Add your resources here, for example: // let redis = builder.add_redis("cache")?; // let postgres = builder.add_postgres("db")?; let app = builder.build()?; app.run(None)?; Ok(()) } """; // Create Cargo.toml files["Cargo.toml"] = """ [package] name = "apphost" version = "0.1.0" edition = "2021" [[bin]] name = "apphost" path = "apphost.rs" [dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" lazy_static = "1.4" # The generated SDK under .aspire/modules is large, and incremental compilation splits it into # thousands of codegen units. On macOS debug info stays in those object files, because # split-debuginfo defaults to "unpacked", so LLDB has to stitch a debug map across all of them # and fails to resolve any type whose definition landed in a different unit. Compiling the # AppHost in one pass keeps the debugger working and is not measurably slower. [profile.dev] incremental = false """; // Create apphost.run.json with random ports var random = request.PortSeed.HasValue ? new Random(request.PortSeed.Value) : Random.Shared; var httpsPort = random.Next(10000, 65000); var httpPort = random.Next(10000, 65000); var otlpPort = random.Next(10000, 65000); var resourceServicePort = random.Next(10000, 65000); files["apphost.run.json"] = $$""" { "profiles": { "https": { "applicationUrl": "https://localhost:{{httpsPort}};http://localhost:{{httpPort}}", "environmentVariables": { "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:{{otlpPort}}", "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:{{resourceServicePort}}" } } } } """; return files; } /// <inheritdoc /> public DetectionResult Detect(string directoryPath) { var appHostPath = Path.Combine(directoryPath, AppHostFileName); if (!File.Exists(appHostPath)) { return DetectionResult.NotFound; } var cargoPath = Path.Combine(directoryPath, "Cargo.toml"); if (!File.Exists(cargoPath)) { return DetectionResult.NotFound; } return DetectionResult.Found(LanguageId, AppHostFileName); } /// <inheritdoc /> public RuntimeSpec GetRuntimeSpec() { return new RuntimeSpec { Language = LanguageId, DisplayName = LanguageDisplayName, CodeGenLanguage = CodeGenTarget, DetectionPatterns = s_detectionPatterns, ExtensionLaunchCapability = LanguageId, // No separate install step - cargo run will build automatically InstallDependencies = null, Execute = new CommandSpec { Command = "cargo", // The binary is named explicitly because the scaffolded manifest declares `[[bin]] apphost` // and a package is free to gain more. A bare `cargo run` is ambiguous the moment a second // [[bin]] target exists and fails with "could not determine which binary to run", which would // stop the app host from starting at all. Naming it here rather than adding `default-run` to // the manifest also fixes app hosts that were scaffolded before this change, since the // command comes from the CLI while the manifest is already on disk. // See https://doc.rust-lang.org/cargo/commands/cargo-run.html Args = ["run", "--bin", AppHostBinaryName, "--"] } }; } }