Expiriments with AvaloniaEdit and tracking document changes

This commit is contained in:
eelke 2025-08-30 19:41:10 +02:00
parent 29a141a971
commit 6325409d25
53 changed files with 643 additions and 627 deletions

View file

@ -1,9 +1,11 @@
namespace pgLabII.PgUtils.ConnectionStrings;
using FluentResults;
namespace pgLabII.PgUtils.ConnectionStrings;
public interface IPqConnectionStringTokenizer
{
bool Eof { get; }
string GetKeyword();
void ConsumeEquals();
string GetValue();
bool IsEof { get; }
Result<string> GetKeyword();
Result ConsumeEquals();
Result<string> GetValue();
}

View file

@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace pgLabII.PgUtils.ConnectionStrings.Pq;
enum Keyword
{
Host,
HostAddr,
Port,
DatabaseName,
UserName,
Password,
}

View file

@ -1,8 +1,12 @@
using System.Collections.ObjectModel;
using FluentResults;
using Npgsql;
namespace pgLabII.PgUtils.ConnectionStrings;
/// <summary>
/// Parser for converting a libpq style connection string into a dictionary of key value pairs
/// </summary>
public ref struct PqConnectionStringParser
{
// Note possible keywords
@ -51,64 +55,38 @@ public ref struct PqConnectionStringParser
).Parse();
}
private readonly IPqConnectionStringTokenizer tokenizer;
private readonly Dictionary<string, string> result = new();
private readonly IPqConnectionStringTokenizer _tokenizer;
private readonly Dictionary<string, string> _result = new();
public PqConnectionStringParser(IPqConnectionStringTokenizer tokenizer)
{
this.tokenizer = tokenizer;
this._tokenizer = tokenizer;
}
public IDictionary<string, string> Parse()
{
result.Clear();
_result.Clear();
while (!tokenizer.Eof)
while (!_tokenizer.IsEof)
ParsePair();
return result;
return _result;
}
private void ParsePair()
private Result ParsePair()
{
string kw = tokenizer.GetKeyword();
tokenizer.ConsumeEquals();
string v = tokenizer.GetValue();
result.Add(kw, v);
//switch (kw)
//{
// case "host":
// case "hostaddr":
// result.Host = v.ToString();
// break;
// case "port":
// result.Port = int.Parse(v);
// break;
// case "dbname":
// result.Database = v.ToString();
// break;
// case "user":
// result.Username = v.ToString();
// break;
// case "password":
// result.Password = v.ToString();
// break;
// case "connect_timeout":
// result.Timeout = int.Parse(v);
// break;
// case "application_name":
// result.ApplicationName = v.ToString();
// break;
// case "options":
// result.Options = v.ToString();
// break;
// case "sslmode":
// result.SslMode = ToSslMode(v);
// break;
// default:
// // Todo what do we do with values we do not support/recognize?
// break;
//}
var kwResult = _tokenizer.GetKeyword();
if (kwResult.IsFailed)
return kwResult.ToResult();
var result = _tokenizer.ConsumeEquals();
if (result.IsFailed)
return result;
var valResult = _tokenizer.GetValue();
if (valResult.IsFailed)
return valResult.ToResult();
_result.Add(kwResult.Value, valResult.Value);
return Result.Ok();
}
private SslMode ToSslMode(ReadOnlySpan<char> v)

View file

@ -1,16 +0,0 @@
namespace pgLabII.PgUtils.ConnectionStrings;
public class PqConnectionStringParserException : Exception
{
public PqConnectionStringParserException()
{
}
public PqConnectionStringParserException(string? message) : base(message)
{
}
public PqConnectionStringParserException(string? message, Exception? innerException) : base(message, innerException)
{
}
}

View file

@ -1,4 +1,5 @@
using System.Text;
using FluentResults;
using static System.Net.Mime.MediaTypeNames;
namespace pgLabII.PgUtils.ConnectionStrings;
@ -8,7 +9,7 @@ public class PqConnectionStringTokenizer : IPqConnectionStringTokenizer
private readonly string input;
private int position = 0;
public bool Eof
public bool IsEof
{
get
{
@ -23,37 +24,38 @@ public class PqConnectionStringTokenizer : IPqConnectionStringTokenizer
position = 0;
}
public string GetKeyword()
public Result<string> GetKeyword()
{
if (Eof)
throw new PqConnectionStringParserException($"Unexpected end of file was expecting a keyword at position {position}");
if (IsEof)
return Result.Fail($"Unexpected end of file was expecting a keyword at position {position}");
return GetString(forKeyword: true);
}
public void ConsumeEquals()
public Result ConsumeEquals()
{
ConsumeWhitespace();
if (position < input.Length && input[position] == '=')
{
position++;
return Result.Ok();
}
else
throw new PqConnectionStringParserException($"Was expecting '=' after keyword at position {position}");
return Result.Fail($"Was expecting '=' after keyword at position {position}");
}
public string GetValue()
public Result<string> GetValue()
{
if (Eof)
throw new PqConnectionStringParserException($"Unexpected end of file was expecting a keyword at position {position}");
if (IsEof)
return Result.Fail($"Unexpected end of file was expecting a keyword at position {position}");
return GetString(forKeyword: false);
}
private string GetString(bool forKeyword)
private Result<string> GetString(bool forKeyword)
{
if (forKeyword && input[position] == '=')
throw new PqConnectionStringParserException($"Unexpected '=' was expecting keyword at position {position}");
return Result.Fail($"Unexpected '=' was expecting keyword at position {position}");
if (input[position] == '\'')
return ParseQuotedText();
@ -75,7 +77,7 @@ public class PqConnectionStringTokenizer : IPqConnectionStringTokenizer
return input.Substring(start, position - start);
}
private string ParseQuotedText()
private Result<string> ParseQuotedText()
{
bool escape = false;
StringBuilder sb = new();
@ -93,7 +95,7 @@ public class PqConnectionStringTokenizer : IPqConnectionStringTokenizer
escape = false;
break;
default:
throw new PqConnectionStringParserException($"Invalid escape sequence at position {position}");
return Result.Fail($"Invalid escape sequence at position {position}");
}
}
else
@ -113,6 +115,6 @@ public class PqConnectionStringTokenizer : IPqConnectionStringTokenizer
}
}
}
throw new PqConnectionStringParserException($"Missing end quote on value starting at {start}");
return Result.Fail($"Missing end quote on value starting at {start}");
}
}

View file

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentResults" />
<PackageReference Include="Npgsql" />
</ItemGroup>