using System.Text; using FluentResults; using static System.Net.Mime.MediaTypeNames; namespace pgLabII.PgUtils.ConnectionStrings; public class PqConnectionStringTokenizer : IPqConnectionStringTokenizer { private readonly string input; private int position = 0; public bool IsEof { get { ConsumeWhitespace(); return position >= input.Length; } } public PqConnectionStringTokenizer(string input) { this.input = input; position = 0; } public Result GetKeyword() { if (IsEof) return Result.Fail($"Unexpected end of file was expecting a keyword at position {position}"); return GetString(forKeyword: true); } public Result ConsumeEquals() { ConsumeWhitespace(); if (position < input.Length && input[position] == '=') { position++; return Result.Ok(); } else return Result.Fail($"Was expecting '=' after keyword at position {position}"); } public Result GetValue() { if (IsEof) return Result.Fail($"Unexpected end of file was expecting a keyword at position {position}"); return GetString(forKeyword: false); } private Result GetString(bool forKeyword) { if (forKeyword && input[position] == '=') return Result.Fail($"Unexpected '=' was expecting keyword at position {position}"); if (input[position] == '\'') return ParseQuotedText(); return UnquotedString(forKeyword); } private void ConsumeWhitespace() { while (position < input.Length && char.IsWhiteSpace(input[position])) position++; } private string UnquotedString(bool forKeyword) { int start = position; while (++position < input.Length && !char.IsWhiteSpace(input[position]) && (!forKeyword || input[position] != '=')) { } return input.Substring(start, position - start); } private Result ParseQuotedText() { bool escape = false; StringBuilder sb = new(); int start = position; while (++position < input.Length) { char c = input[position]; if (escape) { switch (c) { case '\'': case '\\': sb.Append(c); escape = false; break; default: return Result.Fail($"Invalid escape sequence at position {position}"); } } else { if (c == '\'') { ++position; return sb.ToString(); } else if (c == '\\') { escape = true; } else { sb.Append(c); } } } return Result.Fail($"Missing end quote on value starting at {start}"); } }