namespace Houston.Services; public class IniParser { public async Task ParseAsync(TextReader reader, CancellationToken cancellationToken) { var sections = new Dictionary>(StringComparer.OrdinalIgnoreCase); var current = new Dictionary(StringComparer.OrdinalIgnoreCase); sections[String.Empty] = current; while (await reader.ReadLineAsync(cancellationToken) is { } line) { var trimmed = line.Trim(); // Skip blank lines and comments if (trimmed.Length == 0 || trimmed[0] is ';' or '#') continue; if (trimmed.StartsWith('[') && trimmed.EndsWith(']')) { var name = trimmed[1..^1].Trim(); if (!sections.TryGetValue(name, out current!)) sections[name] = current = new Dictionary(StringComparer.OrdinalIgnoreCase); continue; } var separator = trimmed.IndexOf('='); if (separator < 0) continue; var key = trimmed[..separator].Trim(); if (key.Length == 0) continue; // Last value wins for duplicate keys within a section. current[key] = trimmed[(separator + 1)..].Trim(); } return new(sections); } /// /// Parses INI content from a string. /// public Task ParseAsync(string content, CancellationToken cancellationToken) { using var reader = new StringReader(content); return ParseAsync(reader, cancellationToken); }