Skip to content

Commit bfe9470

Browse files
committed
globaloptions no default
1 parent a18b179 commit bfe9470

File tree

8 files changed

+204
-59
lines changed

8 files changed

+204
-59
lines changed

sandbox/GeneratorSandbox/Program.cs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
using ConsoleAppFramework;
2+
using Google.Protobuf.Reflection;
23
using Microsoft.Extensions.DependencyInjection;
34
using Microsoft.Extensions.Logging;
5+
using System.Diagnostics.CodeAnalysis;
46

57
//args = ["--x", "10", "--y", "20", "-v", "--prefix-output", "takoyakix"];
68

79
var app = ConsoleApp.Create();
810

11+
//
12+
// AddGlobalOption
13+
914
app.ConfigureGlobalOptions((ref ConsoleApp.GlobalOptionsBuilder builder) =>
1015
{
11-
var verbose = builder.AddGlobalOption<bool>($"-v", "", true);
12-
var noColor = builder.AddGlobalOption<bool>("--no-color", "Don't colorize output.");
13-
var dryRun = builder.AddGlobalOption<bool>("--dry-run");
16+
var verbose = builder.AddGlobalOption($"-v", "", true);
17+
var noColor = builder.AddGlobalOption("--no-color", "Don't colorize output.");
18+
var dryRun = builder.AddGlobalOption("--dry-run", "");
1419
var prefixOutput = builder.AddRequiredGlobalOption<string>("--prefix-output|-pp|-po", "Prefix output with level.");
1520

21+
// var tako = builder.AddGlobalOption<int>("--in", "");
22+
//var tako = builder.AddGlobalOption<MyFruit>("--fruit", "");
23+
24+
//return new GlobalOptions(true, true, true, "");
1625
return new GlobalOptions(verbose, noColor, dryRun, prefixOutput);
1726
});
1827

@@ -34,8 +43,45 @@
3443
});
3544
});
3645

37-
app.Add("", (int x, int y, [FromServices]GlobalOptions globalOptions) => Console.WriteLine(x + y + ":" + globalOptions));
46+
app.Add<MyCommand>();
47+
48+
// app.Add("", (int x, int y, [FromServices] GlobalOptions globalOptions) => Console.WriteLine(x + y + ":" + globalOptions));
49+
50+
//var iii = int.Parse("1000");
51+
//var sss = new string('a', 3);
52+
//var datet = DateTime.Parse("10000");
53+
54+
// AddGlobalOption<int?>("hoge", "takoyaki", null);
3855

3956
app.Run(args);
4057

58+
59+
// public T AddGlobalOption<T>([ConstantExpected] string name, [ConstantExpected] string description = "", T defaultValue = default(T))
60+
61+
62+
static void AddGlobalOption<T>([ConstantExpected] string name, [ConstantExpected] string description, [ConstantExpected] T defaultValue)
63+
{
64+
}
65+
4166
internal record GlobalOptions(bool Verbose, bool NoColor, bool DryRun, string PrefixOutput);
67+
68+
internal class MyCommand(GlobalOptions globalOptions)
69+
{
70+
/// <summary>
71+
/// my command
72+
/// </summary>
73+
/// <param name="xxx">-x, takoyaki</param>
74+
/// <param name="yyyy">-yy|-y, naninuneno</param>
75+
[Command("")]
76+
public void Run(int xxx, int yyyy, bool z, bool zzz, Int128 iiii, MyFruit myFruit = default, Version version = null)
77+
{
78+
Console.WriteLine(xxx + yyyy + ":" + globalOptions);
79+
}
80+
}
81+
82+
public enum MyFruit
83+
{
84+
Apple, Orange, Grape
85+
}
86+
87+

src/ConsoleAppFramework/Command.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,6 @@ public record class GlobalOptionInfo
473473
public CommandParameter ToDummyCommandParameter()
474474
{
475475
// see: CommandHelpBuilder.CreateCommandHelpDefinition / BuildOptionsMessage
476-
477476
// IsParsable, name + aliases, description, HasDefaultValue/DefaultValue, IsHidden, IsParams
478477

479478
// TODO: IsRequired => DefaultValue == null && !IsParams
@@ -500,8 +499,8 @@ public CommandParameter ToDummyCommandParameter()
500499
HasDefaultValue = DefaultValue != null, // TODO
501500
DefaultValue = DefaultValue,
502501
Description = Description,
503-
Name = Name, // TODO: convert with aliase
504-
Aliases = []
502+
Name = null!, // allows null in CreateCommandHelpDefinition
503+
Aliases = Name.Split(['|'], StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray()
505504
};
506505
}
507506
}

src/ConsoleAppFramework/CommandHelpBuilder.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,10 @@ static CommandHelpDefinition CreateCommandHelpDefinition(Command descriptor)
275275
{
276276
options.Add(alias);
277277
}
278-
options.Add("--" + item.Name);
278+
if (item.Name != null)
279+
{
280+
options.Add("--" + item.Name);
281+
}
279282
}
280283

281284
var description = item.Description;

src/ConsoleAppFramework/ConsoleAppBaseCode.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,12 @@ public GlobalOptionsBuilder(Span<string> args)
570570
this.args = args;
571571
}
572572
573-
public T AddGlobalOption<T>([ConstantExpected] string name, [ConstantExpected] string description = "", T defaultValue = default(T))
573+
public bool AddGlobalOption([ConstantExpected] string name, [ConstantExpected] string description)
574+
{
575+
return AddGlobalOption<bool>(name, description, false);
576+
}
577+
578+
public T AddGlobalOption<T>([ConstantExpected] string name, [ConstantExpected] string description, [ConstantExpected] T defaultValue)
574579
{
575580
var aliasCount = name.AsSpan().Count("|") + 1;
576581
if (aliasCount == 1)
@@ -614,7 +619,7 @@ public GlobalOptionsBuilder(Span<string> args)
614619
return defaultValue;
615620
}
616621
617-
public T AddRequiredGlobalOption<T>([ConstantExpected] string name, [ConstantExpected] string description = "")
622+
public T AddRequiredGlobalOption<T>([ConstantExpected] string name, [ConstantExpected] string description)
618623
{
619624
if (typeof(T) == typeof(bool)) throw new InvalidOperationException("<bool> can not use in AddRequiredGlobalOption. use AddGlobalOption instead.");
620625

src/ConsoleAppFramework/ConsoleAppGenerator.cs

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
297297
{
298298
if (collectBuilderContext.GlobalOptions.Length != 0)
299299
{
300-
// override commandIds
300+
// quick-hack to override commandIds
301301
var globalOptionParameters = collectBuilderContext.GlobalOptions.Select(x => x.ToDummyCommandParameter());
302302
commandIds = commandIds.Select(x =>
303303
{
@@ -500,7 +500,7 @@ public CollectBuilderContext(ConsoleAppFrameworkGeneratorOptions generatorOption
500500

501501
if (configureGlobalOptionsGroup.Count() == 1)
502502
{
503-
503+
// TODO: move to Parser
504504
var configureGlobalOptions = configureGlobalOptionsGroup.First();
505505

506506
var lambdaExpr = (configureGlobalOptions.Item1.Node as InvocationExpressionSyntax);
@@ -549,56 +549,28 @@ public CollectBuilderContext(ConsoleAppFrameworkGeneratorOptions generatorOption
549549
string description = "";
550550
bool isRequired = x.required;
551551
object? defaultValue = null;
552+
bool isBool = false;
552553

553554
if (memberAccess.Name is GenericNameSyntax genericName)
554555
{
555556
var typeArgument = genericName.TypeArgumentList.Arguments[0];
556557
typeSymbol = new(model.GetTypeInfo(typeArgument).Type!); // TODO: not !
557558
}
558-
559-
var arguments = node.ArgumentList.Arguments;
560-
if (arguments.Count >= 1) // string name
559+
else
561560
{
562-
var constant = model.GetConstantValue(arguments[0].Expression); // TODO: check
563-
name = constant.Value!.ToString();
561+
// maybe bool
562+
typeSymbol = new(model.Compilation.GetSpecialType(SpecialType.System_Boolean));
563+
isBool = true;
564564
}
565565

566+
var arguments = node.ArgumentList.Arguments;
567+
name = model.GetConstantValue(arguments[0].Expression).Value!.ToString();
568+
description = model.GetConstantValue(arguments[1].Expression).Value!.ToString();
566569

567-
// TODO: use named argument???
568-
569-
if (arguments.Count >= 2) // string description = ""
570-
{
571-
// is defaultValue???
572-
573-
574-
var constant = model.GetConstantValue(arguments[1].Expression);
575-
description = constant.Value!.ToString();
576-
}
577-
578-
if (!isRequired)
570+
if (!isRequired && !isBool)
579571
{
580-
if (arguments.Count >= 3) // T defaultValue = default(T)
581-
{
582-
var constant = model.GetConstantValue(arguments[2].Expression); // ???
583-
defaultValue = constant.Value!;
584-
}
585-
else
586-
{
587-
// set defaultValue from
588-
//var symbol = model.GetSymbolInfo(node).Symbol;
589-
//if (symbol is IMethodSymbol methodSymbol)
590-
//{
591-
// var parameter = methodSymbol.Parameters[3];
592-
// if (parameter.HasExplicitDefaultValue)
593-
// {
594-
// defaultValue = parameter.ExplicitDefaultValue;
595-
// }
596-
// else
597-
// {
598-
599-
// }
600-
//}
601-
}
572+
var constant = model.GetConstantValue(arguments[2].Expression);
573+
defaultValue = constant.Value!;
602574
}
603575

604576
return new GlobalOptionInfo

src/ConsoleAppFramework/Parser.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,114 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag
172172
.ToArray();
173173
}
174174

175+
public void ParseGlobalOptions()
176+
{
177+
178+
// GlobalOptions allow type is limited. see ConsoleAppBaseCode.TryParse
179+
bool IsParsableType(ITypeSymbol type, Compilation compilation, WellKnownTypes wellKnownTypes)
180+
{
181+
if (type is INamedTypeSymbol { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } namedType)
182+
{
183+
return false;
184+
}
185+
186+
switch (type.SpecialType)
187+
{
188+
case SpecialType.System_String:
189+
case SpecialType.System_Char:
190+
case SpecialType.System_SByte:
191+
case SpecialType.System_Byte:
192+
case SpecialType.System_Int16:
193+
case SpecialType.System_UInt16:
194+
case SpecialType.System_Int32:
195+
case SpecialType.System_UInt32:
196+
case SpecialType.System_Int64:
197+
case SpecialType.System_UInt64:
198+
case SpecialType.System_Single:
199+
case SpecialType.System_Double:
200+
case SpecialType.System_Decimal:
201+
return true;
202+
}
203+
204+
if (type.TypeKind == TypeKind.Enum)
205+
{
206+
return true;
207+
}
208+
209+
var comparer = SymbolEqualityComparer.Default;
210+
if (comparer.Equals(type, wellKnownTypes.Guid)) return true;
211+
if (comparer.Equals(type, wellKnownTypes.DateTime)) return true;
212+
if (comparer.Equals(type, wellKnownTypes.DateTimeOffset)) return true;
213+
if (comparer.Equals(type, wellKnownTypes.TimeOnly)) return true;
214+
if (comparer.Equals(type, wellKnownTypes.DateOnly)) return true;
215+
if (comparer.Equals(type, wellKnownTypes.Version)) return true;
216+
217+
return false;
218+
}
219+
220+
object? GetDefaultValue(ITypeSymbol type)
221+
{
222+
if (type is INamedTypeSymbol { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } namedType)
223+
{
224+
return null;
225+
}
226+
227+
if (type.TypeKind == TypeKind.Enum)
228+
{
229+
var enumType = (INamedTypeSymbol)type;
230+
var underlyingType = enumType.EnumUnderlyingType;
231+
return underlyingType?.SpecialType switch
232+
{
233+
SpecialType.System_Byte => (byte)0,
234+
SpecialType.System_SByte => (sbyte)0,
235+
SpecialType.System_Int16 => (short)0,
236+
SpecialType.System_UInt16 => (ushort)0,
237+
SpecialType.System_Int32 => 0,
238+
SpecialType.System_UInt32 => 0u,
239+
SpecialType.System_Int64 => 0L,
240+
SpecialType.System_UInt64 => 0UL,
241+
_ => 0
242+
};
243+
}
244+
245+
var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
246+
switch (fullName)
247+
{
248+
case "global::System.DateTime":
249+
return default(DateTime); // new DateTime(0)
250+
case "global::System.DateTimeOffset":
251+
return default(DateTimeOffset);
252+
case "global::System.TimeSpan":
253+
return default(TimeSpan);
254+
case "global::System.Guid":
255+
return default(Guid); // Guid.Empty
256+
//case "global::System.DateOnly":
257+
// return default(DateOnly);
258+
//case "global::System.TimeOnly":
259+
// return default(TimeOnly);
260+
}
261+
262+
switch (type.SpecialType)
263+
{
264+
case SpecialType.System_Boolean: return false;
265+
case SpecialType.System_Byte: return (byte)0;
266+
case SpecialType.System_SByte: return (sbyte)0;
267+
case SpecialType.System_Int16: return (short)0;
268+
case SpecialType.System_UInt16: return (ushort)0;
269+
case SpecialType.System_Int32: return 0;
270+
case SpecialType.System_UInt32: return 0u;
271+
case SpecialType.System_Int64: return 0L;
272+
case SpecialType.System_UInt64: return 0UL;
273+
case SpecialType.System_Single: return 0f;
274+
case SpecialType.System_Double: return 0d;
275+
case SpecialType.System_Decimal: return 0m;
276+
case SpecialType.System_Char: return '\0';
277+
default:
278+
return null;
279+
}
280+
}
281+
}
282+
175283
Command? ExpressionToCommand(ExpressionSyntax expression, string commandName)
176284
{
177285
var lambda = expression as ParenthesizedLambdaExpressionSyntax;

src/ConsoleAppFramework/WellKnownTypes.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ public class WellKnownTypes(Compilation compilation)
1313
INamedTypeSymbol? version;
1414
public INamedTypeSymbol Version => version ??= GetTypeByMetadataName("System.Version");
1515

16+
INamedTypeSymbol? dateTime;
17+
public INamedTypeSymbol DateTime => dateTime ??= GetTypeByMetadataName("System.DateTime");
18+
19+
INamedTypeSymbol? timeOnly;
20+
public INamedTypeSymbol TimeOnly => timeOnly ??= GetTypeByMetadataName("System.TimeOnly");
21+
22+
INamedTypeSymbol? dateOnly;
23+
public INamedTypeSymbol DateOnly => dateOnly ??= GetTypeByMetadataName("System.DateOnly");
24+
1625
INamedTypeSymbol? spanParsable;
1726
public INamedTypeSymbol? ISpanParsable => spanParsable ??= compilation.GetTypeByMetadataName("System.ISpanParsable`1");
1827

@@ -35,6 +44,9 @@ public bool HasTryParse(ITypeSymbol type)
3544
{
3645
if (SymbolEqualityComparer.Default.Equals(type, DateTimeOffset)
3746
|| SymbolEqualityComparer.Default.Equals(type, Guid)
47+
|| SymbolEqualityComparer.Default.Equals(type, DateTime)
48+
|| SymbolEqualityComparer.Default.Equals(type, DateOnly)
49+
|| SymbolEqualityComparer.Default.Equals(type, TimeOnly)
3850
|| SymbolEqualityComparer.Default.Equals(type, Version)
3951
)
4052
{

0 commit comments

Comments
 (0)