File: Language\LanguageService.cs
Web Access
Project: src\src\Aspire.Hosting.RemoteHost\Aspire.Hosting.RemoteHost.csproj (Aspire.Hosting.RemoteHost)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using Aspire.Hosting.Ats;
using Microsoft.Extensions.Logging;
using StreamJsonRpc;
 
namespace Aspire.Hosting.RemoteHost.Language;
 
/// <summary>
/// JSON-RPC service for language-specific scaffolding, detection, and runtime configuration.
/// </summary>
internal sealed class LanguageService
{
    private readonly LanguageSupportResolver _resolver;
    private readonly ILogger<LanguageService> _logger;
 
    public LanguageService(
        LanguageSupportResolver resolver,
        ILogger<LanguageService> logger)
    {
        _resolver = resolver;
        _logger = logger;
    }
 
    /// <summary>
    /// Scaffolds a new AppHost project for the specified language.
    /// </summary>
    /// <param name="language">The target language (e.g., "TypeScript", "Python").</param>
    /// <param name="targetPath">The target directory path for the project.</param>
    /// <param name="projectName">Optional project name. If null, derived from directory name.</param>
    /// <returns>A dictionary of relative file paths to file contents.</returns>
    [JsonRpcMethod("scaffoldAppHost")]
    public Dictionary<string, string> ScaffoldAppHost(string language, string targetPath, string? projectName = null)
    {
        _logger.LogDebug(">> scaffoldAppHost({Language}, {TargetPath}, {ProjectName})", language, targetPath, projectName);
        var sw = Stopwatch.StartNew();
 
        try
        {
            var languageSupport = _resolver.GetLanguageSupport(language);
            if (languageSupport == null)
            {
                throw new ArgumentException($"No language support found for: {language}");
            }
 
            var request = new ScaffoldRequest
            {
                TargetPath = targetPath,
                ProjectName = projectName
            };
 
            var files = languageSupport.Scaffold(request);
 
            _logger.LogDebug("<< scaffoldAppHost({Language}) completed in {ElapsedMs}ms, generated {FileCount} files", language, sw.ElapsedMilliseconds, files.Count);
            return files;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "<< scaffoldAppHost({Language}) failed", language);
            throw;
        }
    }
 
    /// <summary>
    /// Detects the language of an AppHost in the specified directory.
    /// </summary>
    /// <param name="directoryPath">The directory to check.</param>
    /// <returns>Detection result with language and file information.</returns>
    [JsonRpcMethod("detectAppHostType")]
    public DetectionResult DetectAppHostType(string directoryPath)
    {
        _logger.LogDebug(">> detectAppHostType({DirectoryPath})", directoryPath);
        var sw = System.Diagnostics.Stopwatch.StartNew();
 
        try
        {
            foreach (var languageSupport in _resolver.GetAllLanguages())
            {
                var result = languageSupport.Detect(directoryPath);
                if (result.IsValid)
                {
                    _logger.LogDebug("<< detectAppHostType({DirectoryPath}) found {Language} in {ElapsedMs}ms", directoryPath, result.Language, sw.ElapsedMilliseconds);
                    return result;
                }
            }
 
            _logger.LogDebug("<< detectAppHostType({DirectoryPath}) not found in {ElapsedMs}ms", directoryPath, sw.ElapsedMilliseconds);
            return DetectionResult.NotFound;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "<< detectAppHostType({DirectoryPath}) failed", directoryPath);
            throw;
        }
    }
 
    /// <summary>
    /// Gets the runtime execution specification for the specified language.
    /// </summary>
    /// <param name="language">The target language (e.g., "TypeScript", "Python").</param>
    /// <returns>The runtime spec containing commands for execution.</returns>
    [JsonRpcMethod("getRuntimeSpec")]
    public RuntimeSpec GetRuntimeSpec(string language)
    {
        _logger.LogDebug(">> getRuntimeSpec({Language})", language);
        var sw = System.Diagnostics.Stopwatch.StartNew();
 
        try
        {
            var languageSupport = _resolver.GetLanguageSupport(language);
            if (languageSupport == null)
            {
                throw new ArgumentException($"No language support found for: {language}");
            }
 
            var spec = languageSupport.GetRuntimeSpec();
 
            _logger.LogDebug("<< getRuntimeSpec({Language}) completed in {ElapsedMs}ms", language, sw.ElapsedMilliseconds);
            return spec;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "<< getRuntimeSpec({Language}) failed", language);
            throw;
        }
    }
}