using System.IO.Abstractions; using System.Text.RegularExpressions; using Common.Extensions; namespace TrashLib.Services.CustomFormat.Guide; public class CustomFormatCategoryParser : ICustomFormatCategoryParser { private static readonly Regex TableRegex = new(@"^\s*\|(.*)\|\s*$"); private static readonly Regex LinkRegex = new(@"^\[(.+?)\]\(#(.+?)\)$"); public ICollection Parse(IFileInfo collectionOfCustomFormatsMdFile) { var columns = new List>(); using var md = collectionOfCustomFormatsMdFile.OpenText(); while (!md.EndOfStream) { var rows = ParseTable(md); // Pivot the data so that we have lists of columns instead of lists of rows // Taken from: https://stackoverflow.com/a/39485441/157971 columns.AddRange(rows .SelectMany(x => x.Select((value, index) => (value, index))) .GroupBy(x => x.index, x => x.value) .Select(x => x.ToList())); } return columns .GroupBy(x => x[0], x => x.Skip(1)) .SelectMany(x => x.SelectMany(y => y).Distinct().Select(y => ParseLink(x.Key, y))) .NotNull() .ToList(); } private static CustomFormatCategoryItem? ParseLink(string categoryName, string markdownLink) { var match = LinkRegex.Match(markdownLink); return match.Success ? new CustomFormatCategoryItem(categoryName, match.Groups[1].Value, match.Groups[2].Value) : null; } private static IEnumerable> ParseTable(TextReader stream) { var tableRows = new List>(); while (true) { var line = stream.ReadLine(); if (line is null) { break; } if (line.Any()) { var fields = GetTableRow(line); if (fields.Any()) { tableRows.Add(fields); continue; } } if (tableRows.Any()) { break; } } return tableRows // Filter out the `|---|---|---|` part of the table between the heading & data rows. .Where(x => !Regex.IsMatch(x[0], @"^-+$")); } private static List GetTableRow(string line) { var fields = new List(); var match = TableRegex.Match(line); if (match.Success) { var tableRow = match.Groups[1].Value; fields = tableRow.Split('|').Select(x => x.Trim()).ToList(); } return fields; } }