NetDocsForLLM/Services/DocFxService.cs

204 lines
6.7 KiB
C#

using NetDocsForLLM.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace NetDocsForLLM.Services
{
public interface IDocFxService
{
Task<string> GenerateMetadataAsync(IEnumerable<AssemblyModel> assemblies);
}
public class DocFxService : IDocFxService
{
private readonly string _workingDirectory;
private readonly string _docfxPath;
public DocFxService()
{
// Create a temporary working directory
_workingDirectory = Path.Combine(Path.GetTempPath(), "NetDocsForLLM_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_workingDirectory);
// Locate DocFX executable in the packages directory
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
_docfxPath = Path.Combine(baseDir, "docfx", "docfx.exe");
// If not found in the default location, try to locate it in the packages directory
if (!File.Exists(_docfxPath))
{
try
{
var packagesDir = Path.Combine(baseDir, "..", "..", "..", "packages");
// Check if packages directory exists before attempting to search it
if (Directory.Exists(packagesDir))
{
var docfxPaths = Directory.GetFiles(packagesDir, "docfx.exe", SearchOption.AllDirectories);
if (docfxPaths.Length > 0)
{
_docfxPath = docfxPaths[0];
}
}
// Check embedded docfx in dotnet tools
if (!File.Exists(_docfxPath))
{
var toolsDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".dotnet", "tools");
if (Directory.Exists(toolsDir))
{
var docfxToolPath = Path.Combine(toolsDir, "docfx.exe");
if (File.Exists(docfxToolPath))
{
_docfxPath = docfxToolPath;
}
}
}
}
catch (DirectoryNotFoundException)
{
// Directory not found, continue to the check below
}
// If still not found, throw an exception
if (!File.Exists(_docfxPath))
{
throw new FileNotFoundException("No se pudo encontrar docfx.exe. Asegúrese de que el paquete docfx.console esté instalado.");
}
}
}
public async Task<string> GenerateMetadataAsync(IEnumerable<AssemblyModel> assemblies)
{
try
{
// Create DocFX configuration
var configPath = Path.Combine(_workingDirectory, "docfx.json");
var config = CreateDocFxConfig(assemblies);
File.WriteAllText(configPath, config);
// Run DocFX metadata
var result = await RunDocFxMetadataAsync(configPath);
// Return path to the generated metadata
var apiPath = Path.Combine(_workingDirectory, "obj", "api");
return apiPath;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error al generar metadatos con DocFX: {ex.Message}", ex);
}
}
private string CreateDocFxConfig(IEnumerable<AssemblyModel> assemblies)
{
var assemblyPaths = new List<string>();
var xmlPaths = new List<string>();
foreach (var assembly in assemblies)
{
assemblyPaths.Add(assembly.FilePath);
if (assembly.HasXmlDocumentation)
{
xmlPaths.Add(assembly.XmlDocPath);
}
}
return $@"
{{
""metadata"": [
{{
""src"": [
{{
""files"": [
""{string.Join("\",\n \"", assemblyPaths.Select(p => p.Replace("\\", "\\\\")))}""
],
""src"": "".""
}}
],
""dest"": ""obj/api"",
""properties"": {{
""TargetFramework"": ""net6.0""
}},
""disableGitFeatures"": true,
""disableDefaultFilter"": false
}}
],
""build"": {{
""content"": [
{{
""files"": [""*.yml""],
""src"": ""obj/api"",
""dest"": ""api""
}}
],
""resource"": [
{{
""files"": [""images/**""],
""exclude"": [""obj/**"", ""_site/**""]
}}
],
""dest"": ""_site"",
""globalMetadataFiles"": [],
""fileMetadataFiles"": [],
""template"": [""default""],
""postProcessors"": [],
""markdownEngineName"": ""markdig"",
""noLangKeyword"": false,
""keepFileLink"": false,
""cleanupCacheHistory"": false,
""disableGitFeatures"": false
}}
}}";
}
private async Task<string> RunDocFxMetadataAsync(string configPath)
{
var startInfo = new ProcessStartInfo
{
FileName = _docfxPath,
Arguments = $"metadata \"{configPath}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = _workingDirectory
};
using var process = new Process { StartInfo = startInfo };
var outputBuilder = new System.Text.StringBuilder();
var errorBuilder = new System.Text.StringBuilder();
process.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
outputBuilder.AppendLine(e.Data);
};
process.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
errorBuilder.AppendLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync();
if (process.ExitCode != 0)
{
throw new InvalidOperationException(
$"DocFX metadata falló con código de salida {process.ExitCode}. Error: {errorBuilder}");
}
return outputBuilder.ToString();
}
}
}