NetDocsForLLM/Services/DocumentationGenerator.cs

374 lines
15 KiB
C#

using NetDocsForLLM.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace NetDocsForLLM.Services
{
public interface IDocumentationGenerator
{
Task<DocumentationModel> GenerateDocumentation(IEnumerable<AssemblyModel> assemblies, ExportSettings settings);
string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings);
}
public class DocumentationGenerator : IDocumentationGenerator
{
private readonly IDocFxService _metadataService;
public DocumentationGenerator(IDocFxService metadataService)
{
_metadataService = metadataService ?? throw new ArgumentNullException(nameof(metadataService));
}
public async Task<DocumentationModel> GenerateDocumentation(IEnumerable<AssemblyModel> assemblies, ExportSettings settings)
{
try
{
// Generate metadata using reflection service
var metadataPath = await _metadataService.GenerateMetadataAsync(assemblies);
// Process metadata files
var documentation = new DocumentationModel();
var namespaces = new Dictionary<string, NamespaceDocumentation>();
// Read JSON files produced by our reflection service
var jsonFiles = Directory.GetFiles(metadataPath, "*.json", SearchOption.AllDirectories);
foreach (var jsonFile in jsonFiles)
{
if (Path.GetFileName(jsonFile).StartsWith("error_"))
continue; // Skip error files
ProcessJsonMetadata(jsonFile, documentation, namespaces, settings);
}
return documentation;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error al generar documentación: {ex.Message}", ex);
}
}
private void ProcessJsonMetadata(string jsonFile, DocumentationModel documentation,
Dictionary<string, NamespaceDocumentation> namespaces,
ExportSettings settings)
{
try
{
// Leer el archivo JSON
var jsonContent = File.ReadAllText(jsonFile);
var typeData = JsonConvert.DeserializeObject<dynamic>(jsonContent);
if (typeData == null)
return;
// Obtener namespace
string namespaceName = typeData.Namespace?.ToString() ?? "Global";
// Buscar o crear el namespace en nuestra documentación
if (!namespaces.TryGetValue(namespaceName, out var namespaceDoc))
{
namespaceDoc = new NamespaceDocumentation
{
Name = namespaceName,
Description = $"Contiene tipos del ensamblado"
};
namespaces[namespaceName] = namespaceDoc;
documentation.Namespaces.Add(namespaceDoc);
}
// Crear documentación de tipo
var typeDoc = new TypeDocumentation
{
Name = typeData.Name,
FullName = typeData.FullName,
Description = typeData.XmlDocumentation?.Summary ?? "Sin documentación disponible",
TypeKind = GetTypeKind(typeData)
};
// Agregar base types e interfaces si están disponibles
if (typeData.BaseType != null && typeData.BaseType.ToString() != "System.Object")
{
typeDoc.BaseTypes.Add(typeData.BaseType.ToString());
}
// Agregar interfaces
if (typeData.Interfaces != null)
{
foreach (var interfaceType in typeData.Interfaces)
{
typeDoc.Interfaces.Add(interfaceType.ToString());
}
}
// Procesar miembros
if (typeData.Members != null)
{
foreach (var member in typeData.Members)
{
// Filtrar miembros privados si la configuración lo indica
if (!settings.IncludePrivateMembers &&
member.IsPublic != null && !(bool)member.IsPublic)
continue;
var memberDoc = new MemberDocumentation
{
Name = member.Name,
Description = member.XmlDocumentation?.Summary ?? "Sin documentación disponible",
MemberType = member.MemberType,
Signature = member.Signature ?? GenerateSignature(member),
ReturnType = GetReturnType(member),
ReturnDescription = member.XmlDocumentation?.Returns ?? ""
};
// Agregar parámetros si es un método
if (member.MemberType?.ToString() == "Method" && member.Parameters != null)
{
foreach (var param in member.Parameters)
{
var paramDoc = new ParameterDocumentation
{
Name = param.Name,
Type = param.Type,
IsOptional = param.IsOptional != null && (bool)param.IsOptional,
DefaultValue = param.DefaultValue?.ToString() ?? "",
Description = GetParameterDescription(member.XmlDocumentation, param.Name?.ToString())
};
memberDoc.Parameters.Add(paramDoc);
}
}
// Agregar ejemplos si están disponibles y configurados
if (settings.IncludeExamples &&
member.XmlDocumentation?.Examples != null)
{
foreach (var example in member.XmlDocumentation.Examples)
{
if (example != null && !string.IsNullOrWhiteSpace(example.ToString()))
memberDoc.Examples.Add(example.ToString());
}
}
typeDoc.Members.Add(memberDoc);
}
}
namespaceDoc.Types.Add(typeDoc);
}
catch (Exception ex)
{
// Si hay error al procesar, lo registramos pero continuamos
Console.WriteLine($"Error al procesar {jsonFile}: {ex.Message}");
}
}
private string GetTypeKind(dynamic typeData)
{
if (typeData.IsClass != null && (bool)typeData.IsClass)
return "Class";
if (typeData.IsInterface != null && (bool)typeData.IsInterface)
return "Interface";
if (typeData.IsEnum != null && (bool)typeData.IsEnum)
return "Enum";
return "Type";
}
private string GenerateSignature(dynamic member)
{
// Generar una firma simple basada en el tipo de miembro
string memberType = member.MemberType?.ToString();
string name = member.Name?.ToString() ?? "Unknown";
if (memberType == "Method")
{
string returnType = member.ReturnType?.ToString() ?? "void";
string parameters = "";
if (member.Parameters != null)
{
var paramList = new List<string>();
foreach (var param in member.Parameters)
{
string paramType = param.Type?.ToString() ?? "object";
string paramName = param.Name?.ToString() ?? "param";
paramList.Add($"{paramType} {paramName}");
}
parameters = string.Join(", ", paramList);
}
return $"{returnType} {name}({parameters})";
}
else if (memberType == "Property")
{
string propType = member.Type?.ToString() ?? "object";
string accessors = "";
bool canRead = member.CanRead != null && (bool)member.CanRead;
bool canWrite = member.CanWrite != null && (bool)member.CanWrite;
if (canRead && canWrite)
accessors = " { get; set; }";
else if (canRead)
accessors = " { get; }";
else if (canWrite)
accessors = " { set; }";
return $"{propType} {name}{accessors}";
}
else if (memberType == "Field")
{
string fieldType = member.Type?.ToString() ?? "object";
return $"{fieldType} {name}";
}
else if (memberType == "Event")
{
string eventType = member.EventHandlerType?.ToString() ?? "EventHandler";
return $"event {eventType} {name}";
}
return name;
}
private string GetReturnType(dynamic member)
{
string memberType = member.MemberType?.ToString();
if (memberType == "Method")
return member.ReturnType?.ToString() ?? "void";
else if (memberType == "Property")
return member.Type?.ToString() ?? "object";
return "";
}
private string GetParameterDescription(dynamic xmlDocumentation, string paramName)
{
if (xmlDocumentation?.Parameters == null || paramName == null)
return "";
foreach (var param in xmlDocumentation.Parameters)
{
if (param.Name?.ToString() == paramName)
return param.Description?.ToString() ?? "";
}
return "";
}
public string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings)
{
try
{
if (settings.OutputFormat == OutputFormat.Json)
{
return JsonConvert.SerializeObject(documentation, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
});
}
else // YAML
{
// En un escenario real, usaríamos YamlDotNet
// Para esta implementación, usaremos una conversión manual simplificada
return ConvertJsonToSimpleYaml(
JsonConvert.SerializeObject(documentation, Formatting.None,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
}));
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error al generar vista previa: {ex.Message}", ex);
}
}
private string ConvertJsonToSimpleYaml(string json)
{
// Deserialize JSON
var obj = JsonConvert.DeserializeObject<dynamic>(json);
// Basic YAML builder
var yaml = new System.Text.StringBuilder();
yaml.AppendLine("namespaces:");
foreach (var ns in obj.Namespaces)
{
yaml.AppendLine($" - name: {ns.Name}");
yaml.AppendLine($" description: {ns.Description}");
yaml.AppendLine(" types:");
foreach (var type in ns.Types)
{
yaml.AppendLine($" - name: {type.Name}");
yaml.AppendLine($" fullName: {type.FullName}");
yaml.AppendLine($" typeKind: {type.TypeKind}");
yaml.AppendLine($" description: {type.Description}");
yaml.AppendLine(" members:");
foreach (var member in type.Members)
{
yaml.AppendLine($" - name: {member.Name}");
yaml.AppendLine($" memberType: {member.MemberType}");
yaml.AppendLine($" signature: \"{EscapeYamlString(member.Signature)}\"");
yaml.AppendLine($" description: \"{EscapeYamlString(member.Description)}\"");
if (member.Parameters != null && member.Parameters.Count > 0)
{
yaml.AppendLine(" parameters:");
foreach (var param in member.Parameters)
{
yaml.AppendLine($" - name: {param.Name}");
yaml.AppendLine($" type: {param.Type}");
yaml.AppendLine($" description: \"{EscapeYamlString(param.Description)}\"");
yaml.AppendLine($" isOptional: {param.IsOptional.ToString().ToLower()}");
if (!string.IsNullOrEmpty(param.DefaultValue))
yaml.AppendLine($" defaultValue: \"{EscapeYamlString(param.DefaultValue)}\"");
}
}
if (!string.IsNullOrEmpty(member.ReturnType))
{
yaml.AppendLine($" returnType: {member.ReturnType}");
if (!string.IsNullOrEmpty(member.ReturnDescription))
yaml.AppendLine($" returnDescription: \"{EscapeYamlString(member.ReturnDescription)}\"");
}
if (member.Examples != null && member.Examples.Count > 0)
{
yaml.AppendLine(" examples:");
foreach (var example in member.Examples)
{
yaml.AppendLine($" - |\n {example.Replace("\n", "\n ")}");
}
}
}
}
}
return yaml.ToString();
}
private string EscapeYamlString(string input)
{
if (string.IsNullOrEmpty(input))
return "";
return input
.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t");
}
}
}