Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

143 rindas
4.8KB

  1. using System.Globalization;
  2. namespace Campaign_Tracker.Server.LegacyData.Schema;
  3. /// <summary>
  4. /// Parses the Access schema text dump shipped at
  5. /// <c>Initial Documents/Access_Schema.txt</c> into a strongly-typed
  6. /// <see cref="LegacySchemaBaseline"/> for compatibility checking.
  7. ///
  8. /// File format (one table per block):
  9. /// <code>
  10. /// Table: Contacts
  11. /// ---------------
  12. /// Column: ID Type: 3 Size: Nullable: False
  13. /// Column: EMAIL Type: 130 Size: 255 Nullable: True
  14. /// </code>
  15. /// </summary>
  16. public static class LegacySchemaBaselineParser
  17. {
  18. public static LegacySchemaBaseline ParseFile(string filePath, DateTimeOffset capturedAt)
  19. {
  20. if (!File.Exists(filePath))
  21. {
  22. throw new FileNotFoundException(
  23. $"Legacy schema baseline file not found: {filePath}", filePath);
  24. }
  25. var text = File.ReadAllText(filePath);
  26. return Parse(text, filePath, capturedAt);
  27. }
  28. public static LegacySchemaBaseline Parse(string text, string source, DateTimeOffset capturedAt)
  29. {
  30. var tables = new List<LegacyTableDefinition>();
  31. string? currentTable = null;
  32. var currentColumns = new List<LegacyColumnDefinition>();
  33. foreach (var rawLine in text.Split('\n'))
  34. {
  35. var line = rawLine.TrimEnd('\r').TrimEnd();
  36. if (line.Length == 0) continue;
  37. if (line.All(c => c == '-')) continue;
  38. if (line.StartsWith("Table:", StringComparison.Ordinal))
  39. {
  40. FlushTable(tables, currentTable, currentColumns);
  41. currentTable = line["Table:".Length..].Trim();
  42. currentColumns = new List<LegacyColumnDefinition>();
  43. continue;
  44. }
  45. var trimmed = line.TrimStart();
  46. if (!trimmed.StartsWith("Column:", StringComparison.Ordinal))
  47. {
  48. throw new FormatException($"Unrecognized legacy schema line: {line}");
  49. }
  50. currentColumns.Add(ParseColumn(trimmed));
  51. }
  52. FlushTable(tables, currentTable, currentColumns);
  53. if (tables.Count == 0)
  54. {
  55. throw new FormatException("Legacy schema baseline did not contain any table definitions.");
  56. }
  57. return new LegacySchemaBaseline(tables, source, capturedAt);
  58. }
  59. private static void FlushTable(
  60. List<LegacyTableDefinition> sink,
  61. string? tableName,
  62. List<LegacyColumnDefinition> columns)
  63. {
  64. if (string.IsNullOrWhiteSpace(tableName) || columns.Count == 0) return;
  65. sink.Add(new LegacyTableDefinition(tableName, columns.ToArray()));
  66. }
  67. private static LegacyColumnDefinition ParseColumn(string line)
  68. {
  69. // "Column: NAME Type: 130 Size: 255 Nullable: True"
  70. var name = ReadField(line, "Column:", ["Type:"]);
  71. var typeRaw = ReadField(line, "Type:", ["Size:", "Nullable:", "Constraints:"]);
  72. var sizeRaw = ReadField(line, "Size:", ["Nullable:", "Constraints:"]);
  73. var nullableRaw = ReadField(line, "Nullable:", ["Constraints:"]);
  74. var constraintsRaw = ReadField(line, "Constraints:", []);
  75. if (!int.TryParse(typeRaw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var typeCode))
  76. {
  77. throw new FormatException($"Invalid Type code in column line: {line}");
  78. }
  79. int? size = null;
  80. if (!string.IsNullOrWhiteSpace(sizeRaw))
  81. {
  82. if (!int.TryParse(sizeRaw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedSize))
  83. {
  84. throw new FormatException($"Invalid Size value in column line: {line}");
  85. }
  86. size = parsedSize;
  87. }
  88. if (!bool.TryParse(nullableRaw, out var nullable))
  89. {
  90. throw new FormatException($"Invalid Nullable value in column line: {line}");
  91. }
  92. return new LegacyColumnDefinition(name, typeCode, size, nullable)
  93. {
  94. Constraints = ParseConstraints(constraintsRaw),
  95. };
  96. }
  97. private static string ReadField(string line, string label, string[] terminators)
  98. {
  99. var start = line.IndexOf(label, StringComparison.Ordinal);
  100. if (start < 0) return string.Empty;
  101. start += label.Length;
  102. var end = line.Length;
  103. foreach (var terminator in terminators)
  104. {
  105. var t = line.IndexOf(terminator, start, StringComparison.Ordinal);
  106. if (t >= 0 && t < end) end = t;
  107. }
  108. return line[start..end].Trim();
  109. }
  110. private static IReadOnlyList<string> ParseConstraints(string value)
  111. {
  112. if (string.IsNullOrWhiteSpace(value))
  113. {
  114. return [];
  115. }
  116. return value
  117. .Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
  118. .ToArray();
  119. }
  120. }

Powered by TurnKey Linux.