EnumSourceGenerator.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using System.Linq;
  2. using Microsoft.CodeAnalysis;
  3. namespace QuestPDF.InteropGenerators;
  4. internal class EnumSourceGenerator(string targetNamespace) : ISourceGenerator
  5. {
  6. private string TargetNamespace { get; } = targetNamespace;
  7. public string GenerateCSharpCode(INamespaceSymbol namespaceSymbol)
  8. {
  9. // Enums don't require C ABI interop code generation
  10. // They are passed as integers across the FFI boundary
  11. return string.Empty;
  12. }
  13. public string GeneratePythonCode(INamespaceSymbol namespaceSymbol)
  14. {
  15. var enumSymbol = FindEnumSymbol(namespaceSymbol, TargetNamespace);
  16. if (enumSymbol == null)
  17. return $"# Enum not found: {TargetNamespace}";
  18. var code = new System.Text.StringBuilder();
  19. // Generate Python enum class
  20. code.AppendLine($"class {enumSymbol.Name}(IntEnum):");
  21. // Add class docstring from enum documentation
  22. var enumDocumentation = DocumentationHelper.ExtractDocumentation(enumSymbol.GetDocumentationCommentXml());
  23. if (!string.IsNullOrEmpty(enumDocumentation))
  24. {
  25. code.AppendLine(" \"\"\"");
  26. code.AppendLine($" {enumDocumentation}");
  27. code.AppendLine(" \"\"\"");
  28. code.AppendLine();
  29. }
  30. var members = enumSymbol.GetMembers().OfType<IFieldSymbol>().ToList();
  31. if (members.Count == 0)
  32. {
  33. code.AppendLine(" pass");
  34. }
  35. else
  36. {
  37. for (int i = 0; i < members.Count; i++)
  38. {
  39. var member = members[i];
  40. var value = member.HasConstantValue ? member.ConstantValue : 0;
  41. // Add blank line between members for readability
  42. if (i > 0)
  43. code.AppendLine();
  44. // Add member with value
  45. code.AppendLine($" {NamingHelper.ToSnakeCase(member.Name)} = {value}");
  46. // Add member documentation as docstring (visible in IDE IntelliSense)
  47. var memberDoc = DocumentationHelper.ExtractDocumentation(member.GetDocumentationCommentXml());
  48. if (!string.IsNullOrEmpty(memberDoc))
  49. {
  50. // Use triple-quoted docstring with consistent multi-line format
  51. // This makes it visible in PyCharm and other IDE tooltips
  52. code.AppendLine(" \"\"\"");
  53. // Handle multi-line documentation
  54. var docLines = memberDoc.Split(new[] { '\n' }, System.StringSplitOptions.RemoveEmptyEntries);
  55. foreach (var line in docLines)
  56. {
  57. code.AppendLine($" {line.Trim()}");
  58. }
  59. code.AppendLine(" \"\"\"");
  60. }
  61. }
  62. }
  63. code.AppendLine();
  64. return code.ToString();
  65. }
  66. private static INamedTypeSymbol? FindEnumSymbol(INamespaceSymbol rootNamespace, string fullyQualifiedName)
  67. {
  68. // Split the fully qualified name into parts
  69. var parts = fullyQualifiedName.Split('.');
  70. // Navigate to the target namespace
  71. var currentNamespace = rootNamespace;
  72. for (int i = 0; i < parts.Length - 1; i++)
  73. {
  74. var nextNamespace = currentNamespace.GetNamespaceMembers()
  75. .FirstOrDefault(ns => ns.Name == parts[i]);
  76. if (nextNamespace == null)
  77. return null;
  78. currentNamespace = nextNamespace;
  79. }
  80. // Find the enum type
  81. var enumName = parts[parts.Length - 1];
  82. return currentNamespace.GetTypeMembers()
  83. .FirstOrDefault(t => t.Name == enumName && t.TypeKind == TypeKind.Enum);
  84. }
  85. }