Skip to content

Commit 96a3121

Browse files
committed
fix parse failure
1 parent afcde2b commit 96a3121

File tree

6 files changed

+80
-29
lines changed

6 files changed

+80
-29
lines changed

ReadMe.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,26 @@ internal static partial class ConsoleApp
4242
{
4343
case "--foo":
4444
{
45-
if (!int.TryParse(args[++i], out arg0)) { ThrowArgumentParseFailed("foo", args[i]); }
45+
if (!TryIncrementIndex(ref i, args.Length) || !int.TryParse(args[i], out arg0)) { ThrowArgumentParseFailed("foo", args[i]); }
4646
arg0Parsed = true;
4747
break;
4848
}
4949
case "--bar":
5050
{
51-
if (!int.TryParse(args[++i], out arg1)) { ThrowArgumentParseFailed("bar", args[i]); }
51+
if (!TryIncrementIndex(ref i, args.Length) || !int.TryParse(args[i], out arg1)) { ThrowArgumentParseFailed("bar", args[i]); }
5252
arg1Parsed = true;
5353
break;
5454
}
5555
default:
5656
if (string.Equals(name, "--foo", StringComparison.OrdinalIgnoreCase))
5757
{
58-
if (!int.TryParse(args[++i], out arg0)) { ThrowArgumentParseFailed("foo", args[i]); }
58+
if (!TryIncrementIndex(ref i, args.Length) || !int.TryParse(args[i], out arg0)) { ThrowArgumentParseFailed("foo", args[i]); }
5959
arg0Parsed = true;
6060
break;
6161
}
6262
if (string.Equals(name, "--bar", StringComparison.OrdinalIgnoreCase))
6363
{
64-
if (!int.TryParse(args[++i], out arg1)) { ThrowArgumentParseFailed("bar", args[i]); }
64+
if (!TryIncrementIndex(ref i, args.Length) || !int.TryParse(args[i], out arg1)) { ThrowArgumentParseFailed("bar", args[i]); }
6565
arg1Parsed = true;
6666
break;
6767
}
@@ -88,6 +88,17 @@ internal static partial class ConsoleApp
8888
}
8989
}
9090

91+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
92+
static bool TryIncrementIndex(ref int index, int length)
93+
{
94+
if (index < length)
95+
{
96+
index++;
97+
return true;
98+
}
99+
return false;
100+
}
101+
91102
static partial void ShowHelp(int helpId)
92103
{
93104
Log("""

sandbox/GeneratorSandbox/Program.cs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,27 @@
1111
using ZLogger;
1212

1313

14-
args = "--first-arg invalid.email --second-arg 10".Split(' ');
14+
//args = "--first-arg invalid.email --second-arg 10".Split(' ');
1515

16-
ConsoleApp.Timeout = Timeout.InfiniteTimeSpan;
16+
//ConsoleApp.Timeout = Timeout.InfiniteTimeSpan;
1717

1818

1919

2020

21-
ConsoleApp.Run(args, (
22-
[Argument] DateTime dateTime, // Argument
23-
[Argument] Guid guidvalue, //
24-
int intVar, // required
25-
bool boolFlag, // flag
26-
MyEnum enumValue, // enum
27-
int[] array, // array
28-
MyClass obj, // object
29-
string optional = "abcde", // optional
30-
double? nullableValue = null, // nullable
31-
params string[] paramsArray // params
32-
) => { });
33-
34-
35-
21+
//ConsoleApp.Run(args, (
22+
// [Argument] DateTime dateTime, // Argument
23+
// [Argument] Guid guidvalue, //
24+
// int intVar, // required
25+
// bool boolFlag, // flag
26+
// MyEnum enumValue, // enum
27+
// int[] array, // array
28+
// MyClass obj, // object
29+
// string optional = "abcde", // optional
30+
// double? nullableValue = null, // nullable
31+
// params string[] paramsArray // params
32+
// ) => { });
3633

34+
ConsoleApp.Run(args, (int foo, int bar) => Console.WriteLine($"Sum: {foo + bar}"));
3735

3836

3937

src/ConsoleAppFramework/Command.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Data.Common;
44
using System.Diagnostics.CodeAnalysis;
5+
using System.Reflection;
56
using System.Reflection.Metadata;
67
using System.Text;
78

@@ -168,9 +169,10 @@ public record class CommandParameter
168169
public required string Description { get; init; }
169170
public bool RequireCheckArgumentParsed => !(HasDefaultValue || IsParams || IsFlag);
170171

172+
// increment = false when passed from [Argument]
171173
public string BuildParseMethod(int argCount, string argumentName, WellKnownTypes wellKnownTypes, bool increment)
172174
{
173-
var index = increment ? "++i" : "i";
175+
var incrementIndex = increment ? "!TryIncrementIndex(ref i, args.Length) || " : "";
174176
return Core(Type, false);
175177

176178
string Core(ITypeSymbol type, bool nullable)
@@ -190,13 +192,22 @@ string Core(ITypeSymbol type, bool nullable)
190192

191193
if (CustomParserType != null)
192194
{
193-
return $"if (!{CustomParserType.ToFullyQualifiedFormatDisplayString()}.TryParse(args[{index}], {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
195+
return $"if ({incrementIndex}!{CustomParserType.ToFullyQualifiedFormatDisplayString()}.TryParse(args[i], {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
194196
}
195197

196198
switch (type.SpecialType)
197199
{
198200
case SpecialType.System_String:
199-
return $"arg{argCount} = args[{index}];"; // no parse
201+
// no parse
202+
if (increment)
203+
{
204+
return $"if (!TryIncrementIndex(ref i, args.Length)) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }} else {{ arg{argCount} = args[i]; }}";
205+
}
206+
else
207+
{
208+
return $"arg{argCount} = args[i];";
209+
}
210+
200211
case SpecialType.System_Boolean:
201212
return $"arg{argCount} = true;"; // bool is true flag
202213
case SpecialType.System_Char:
@@ -218,7 +229,7 @@ string Core(ITypeSymbol type, bool nullable)
218229
// Enum
219230
if (type.TypeKind == TypeKind.Enum)
220231
{
221-
return $"if (!Enum.TryParse<{type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>(args[{index}], true, {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
232+
return $"if ({incrementIndex}!Enum.TryParse<{type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>(args[i], true, {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
222233
}
223234

224235
// ParamsArray
@@ -236,7 +247,7 @@ string Core(ITypeSymbol type, bool nullable)
236247
{
237248
if (elementType.AllInterfaces.Any(x => x.EqualsUnconstructedGenericType(parsable)))
238249
{
239-
return $"if (!TrySplitParse(args[{index}], {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
250+
return $"if ({incrementIndex}!TrySplitParse(args[i], {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
240251
}
241252
}
242253
break;
@@ -260,15 +271,15 @@ string Core(ITypeSymbol type, bool nullable)
260271

261272
if (tryParseKnownPrimitive)
262273
{
263-
return $"if (!{type.ToFullyQualifiedFormatDisplayString()}.TryParse(args[{index}], {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
274+
return $"if ({incrementIndex}!{type.ToFullyQualifiedFormatDisplayString()}.TryParse(args[i], {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
264275
}
265276
else if (tryParseIParsable)
266277
{
267-
return $"if (!{type.ToFullyQualifiedFormatDisplayString()}.TryParse(args[{index}], null, {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
278+
return $"if ({incrementIndex}!{type.ToFullyQualifiedFormatDisplayString()}.TryParse(args[i], null, {outArgVar})) {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}{elseExpr}";
268279
}
269280
else
270281
{
271-
return $"try {{ arg{argCount} = System.Text.Json.JsonSerializer.Deserialize<{type.ToFullyQualifiedFormatDisplayString()}>(args[{index}]); }} catch {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}";
282+
return $"try {{ arg{argCount} = System.Text.Json.JsonSerializer.Deserialize<{type.ToFullyQualifiedFormatDisplayString()}>(args[{(increment ? "++i" : "i")}]); }} catch {{ ThrowArgumentParseFailed(\"{argumentName}\", args[i]); }}";
272283
}
273284
}
274285
}

src/ConsoleAppFramework/ConsoleAppGenerator.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,17 @@ static void ThrowArgumentNameNotFound(string argumentName)
188188
throw new ArgumentException($"Argument '{argumentName}' does not found in command prameters.");
189189
}
190190
191+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
192+
static bool TryIncrementIndex(ref int index, int length)
193+
{
194+
if ((index + 1) < length)
195+
{
196+
index += 1;
197+
return true;
198+
}
199+
return false;
200+
}
201+
191202
static bool TryParseParamsArray<T>(ReadOnlySpan<string> args, ref T[] result, ref int i)
192203
where T : IParsable<T>
193204
{

tests/ConsoleAppFramework.GeneratorTests/CSharpGeneratorRunner.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,20 @@ public void Execute(string code, string args, string expected, [CallerArgumentEx
150150
stdout.Should().Be(expected);
151151
}
152152

153+
public string Error(string code, string args, [CallerArgumentExpression("code")] string? codeExpr = null)
154+
{
155+
output.WriteLine(codeExpr);
156+
157+
var (compilation, diagnostics, stdout) = CSharpGeneratorRunner.CompileAndExecute(code, args == "" ? [] : args.Split(' '));
158+
foreach (var item in diagnostics)
159+
{
160+
output.WriteLine(item.ToString());
161+
}
162+
OutputGeneratedCode(compilation);
163+
164+
return stdout;
165+
}
166+
153167
string GetLocationText(Diagnostic diagnostic)
154168
{
155169
var location = diagnostic.Location;

tests/ConsoleAppFramework.GeneratorTests/RunTest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ public void SyncRun()
2020
verifier.Execute("ConsoleApp.Run(args, (int x, int y) => { Console.Write((x + y)); });", "--x 10 --y 20", "30");
2121
}
2222

23+
[Fact]
24+
public void SyncRunShouldFailed()
25+
{
26+
verifier.Error("ConsoleApp.Run(args, (int x) => { Console.Write((x)); });", "--x").Should().Contain("Argument 'x' parse failed.");
27+
}
28+
2329
[Fact]
2430
public void ValidateOne()
2531
{

0 commit comments

Comments
 (0)