using System;
using System.IO;
using System.Text.RegularExpressions;
namespace Migrato.Core;
///
/// Parses a "-- +migrato Up" / "-- +migrato Down" annotated SQL file into a
///
public static partial class MigrationParser
{
private const string UpMarker = "-- +migrato Up";
private const string DownMarker = "-- +migrato Down";
///
/// Parses the migration file at
///
/// The file name or contents do not follow Migrato's conventions
public static Migration Parse(string path)
{
var fileName = Path.GetFileName(path);
var match = FileNameRegex().Match(fileName);
if (!match.Success)
{
throw new MigrationFormatException(
$"'{fileName}' is not a valid migration file name; expected '<14-digit version>_.sql'");
}
var version = long.Parse(match.Groups["version"].Value);
var name = match.Groups["name"].Value;
var content = File.ReadAllText(path);
var (upSql, downSql) = ParseSections(content, fileName);
return new Migration(version)
{
Source = path,
Name = name,
UpSql = upSql,
DownSql = downSql,
};
}
private static (string UpSql, string DownSql) ParseSections(string content, string fileName)
{
var lines = content.Replace("\r\n", "\n").Split('\n');
var upIndex = -1;
var downIndex = -1;
for (var i = 0; i < lines.Length; i++)
{
var trimmed = lines[i].Trim();
if (trimmed.Equals(UpMarker, StringComparison.OrdinalIgnoreCase))
{
if (upIndex != -1)
{
throw new MigrationFormatException($"'{fileName}' has more than one '{UpMarker}' marker");
}
upIndex = i;
}
else if (trimmed.Equals(DownMarker, StringComparison.OrdinalIgnoreCase))
{
if (downIndex != -1)
{
throw new MigrationFormatException($"'{fileName}' has more than one '{DownMarker}' marker");
}
downIndex = i;
}
}
if (upIndex == -1)
{
throw new MigrationFormatException($"'{fileName}' is missing a '{UpMarker}' marker");
}
if (downIndex == -1)
{
throw new MigrationFormatException($"'{fileName}' is missing a '{DownMarker}' marker");
}
if (downIndex < upIndex)
{
throw new MigrationFormatException($"'{fileName}' has '{DownMarker}' before '{UpMarker}'");
}
var upSql = string.Join('\n', lines[(upIndex + 1)..downIndex]).Trim();
var downSql = string.Join('\n', lines[(downIndex + 1)..]).Trim();
return (upSql, downSql);
}
[GeneratedRegex(@"^(?\d{14})_(?.+)\.sql$")]
private static partial Regex FileNameRegex();
}