Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 55 additions & 63 deletions samples/ChatSample.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace ChatSample.Cli;

internal static partial class Program
{
private sealed record TypeInfo(Type Type, Func<string, object> Parse);
private sealed record TypeInfo(Type Type, Func<string, object> Parse, Regex Regex);

[GeneratedRegex(@"^load\s+(.+)$", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex LoadRegex();
Expand Down Expand Up @@ -50,12 +50,26 @@ private sealed record TypeInfo(Type Type, Func<string, object> Parse);
[GeneratedRegex(@"^listen\s+(\w+)(?:\(((?:\s*\w+\s*,)*\s*\w+)?\s*\))?$", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex ListenRegex();

[GeneratedRegex("^-?\\d+")]
private static partial Regex IntegerRegex();
[GeneratedRegex(@"^\""(?:\\.|[^\\])*?\""")]
private static partial Regex StringRegex();
[GeneratedRegex("^true|false", RegexOptions.IgnoreCase)]
private static partial Regex BooleanRegex();
[GeneratedRegex("^-?(?:\\d+)?\\.?\\d+")]
private static partial Regex DoubleRegex();
[GeneratedRegex("^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?")]
private static partial Regex BytesRegex();
[GeneratedRegex("^\\s*(?:,\\s*|$)")]
private static partial Regex ParameterEndRegex();

private static readonly Dictionary<string, TypeInfo> TypeMap = new(StringComparer.OrdinalIgnoreCase)
{
{ "int", new(typeof(int), x => int.Parse(x, null)) },
{ "string", new(typeof(string), Escape) },
{ "bool", new(typeof(bool), x => bool.Parse(x)) },
{ "double", new(typeof(double), x => double.Parse(x, null)) }
{ "int", new(typeof(int), x => int.Parse(x, null), IntegerRegex()) },
{ "string", new(typeof(string), Escape, StringRegex()) },
{ "bool", new(typeof(bool), x => bool.Parse(x), BooleanRegex()) },
{ "double", new(typeof(double), x => double.Parse(x, null), DoubleRegex()) },
{ "binary", new(typeof(byte[]), Convert.FromBase64String, BytesRegex()) },
};

private static string Escape(string x)
Expand All @@ -66,7 +80,7 @@ private static string Escape(string x)
x = x[1..^1];
}
// replace escaped characters
return x.Replace("\\\"", "\"").Replace("\\'", "'").Replace("\\r", "\r").Replace("\\n", "\n").Replace("\\t", "\t");
return x.Replace("\\\"", "\"").Replace("\\'", "'").Replace("\\r", "\r").Replace("\\n", "\n").Replace("\\t", "\t").Replace("\\\\", "\\");
}

private static readonly Dictionary<string, TypeInfo[]> MethodDefineMap = new(StringComparer.OrdinalIgnoreCase);
Expand Down Expand Up @@ -300,33 +314,53 @@ private static async Task Send(IClientProxy proxy, string target, string args)
Console.WriteLine($"Undefined target: {target}, please define it first, see define.");
return;
}
var argStrings = args.Split(',').Select(x => x.Trim()).ToList();

if (argStrings.Count != argTypes.Length)
if (!ParseArguments(target, args, argTypes, out var argValues))
{
Console.WriteLine($"Invalid number of arguments for {target}: {argStrings.Count} != {argTypes.Length}");
return;
}
var argValues = new object[argTypes.Length];
var isValid = true;
await proxy.SendCoreAsync(target, argValues);
}

private static bool ParseArguments(string target, string args, TypeInfo[] argTypes, out object[] argValues)
{
argValues = new object[argTypes.Length];
var currentArgs = args.Trim();
for (var i = 0; i < argTypes.Length; i++)
{
if (string.IsNullOrEmpty(currentArgs))
{
Console.WriteLine($"Invalid number of arguments for {target}: {argTypes.Length} != {i}");
return false;
}
var match = argTypes[i].Regex.Match(currentArgs);
if (!match.Success)
{
Console.WriteLine($"Invalid argument type for {target}: {argTypes[i].Type.Name}");
return false;
}
currentArgs = currentArgs[match.Length..];
var end = ParameterEndRegex().Match(currentArgs);
if (end.Success)
{
currentArgs = currentArgs[end.Length..];
}
else
{
Console.WriteLine($"Invalid argument type for {target}: {argTypes[i].Type.Name}");
return false;
}
try
{
argValues[i] = argTypes[i].Parse(argStrings[i]);
argValues[i] = argTypes[i].Parse(match.Value);
}
catch (Exception)
{
Console.WriteLine($"Invalid argument type for {target}: {argValues[i]}");
isValid = false;
break;
}
}
if (!isValid)
{
return;
}
await proxy.SendCoreAsync(target, argValues);

return true;
}

private static Task NewStream(ServiceHubContext hubContext, Match match)
Expand Down Expand Up @@ -472,28 +506,7 @@ async Task ClientSend(Match match)
Console.WriteLine($"Please define the target first.");
return;
}
var argStrings = args.Split(',').Select(x => x.Trim()).ToList();
if (argStrings.Count != argTypes.Length)
{
Console.WriteLine($"Invalid number of arguments for {target}: {argStrings.Count} != {argTypes.Length}");
return;
}
var argValues = new object[argTypes.Length];
var isValid = true;
for (var i = 0; i < argTypes.Length; i++)
{
try
{
argValues[i] = argTypes[i].Parse(argStrings[i]);
}
catch (Exception)
{
Console.WriteLine($"Invalid argument type for {target}: {argValues[i]}");
isValid = false;
break;
}
}
if (!isValid)
if (!ParseArguments(target, args, argTypes, out var argValues))
{
return;
}
Expand All @@ -512,28 +525,7 @@ async Task ClientStream(Match match)
Console.WriteLine($"Please define the target first.");
return;
}
var argStrings = args.Split(',').Select(x => x.Trim()).ToList();
if (argStrings.Count != sig.Params.Length)
{
Console.WriteLine($"Invalid number of arguments for {target}: {argStrings.Count} != {sig.Params.Length}");
return;
}
var argValues = new object[sig.Params.Length];
var isValid = true;
for (var i = 0; i < sig.Params.Length; i++)
{
try
{
argValues[i] = sig.Params[i].Parse(argStrings[i]);
}
catch (Exception)
{
Console.WriteLine($"Invalid argument type for {target}: {argValues[i]}");
isValid = false;
break;
}
}
if (!isValid)
if (!ParseArguments(target, args, sig.Params, out var argValues))
{
return;
}
Expand Down