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 GenerateDocumentation(IEnumerable 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 GenerateDocumentation(IEnumerable 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(); // 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 namespaces, ExportSettings settings) { try { // Leer el archivo JSON var jsonContent = File.ReadAllText(jsonFile); var typeData = JsonConvert.DeserializeObject(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(); 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(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"); } } }