Skip to content

Commit

Permalink
Added support for command handlers or command as delegates without th…
Browse files Browse the repository at this point in the history
…e async keyword

- Updated to latest daily build 2.0.0-beta4.24201.1 of System.CommandLine.
- Added support for command handlers or command as delegates without the async keyword (which returns Task or Task<int>).
  • Loading branch information
calacayir committed Apr 2, 2024
1 parent cbd2e43 commit adbd9f1
Show file tree
Hide file tree
Showing 225 changed files with 1,005 additions and 246 deletions.
73 changes: 73 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,79 @@ The class that `CliCommand` attribute is applied to,
- will be a root command if the class is not a nested class and `Parent`property is not set.
- will be a sub command if the class is a nested class or `Parent` property is set.

#### Accessing parent commands

Sub-commands can get a reference to the parent command by adding a property of the parent command type.
Alternatively `ParseResult.Bind<TDefinition>` method can be called to manually get reference to a parent command.
Note that binding will be done only once per definition class, so calling this method consecutively
for the same definition class will return the cached result.

```c#
// Sub-commands can get a reference to the parent command by adding a property of the parent command type.
[CliCommand(Description = "A root cli command with children that can access parent commands")]
public class ParentCommandAccessorCliCommand
{
[CliOption(
Description = "This is a global option (Recursive option on the root command), it can appear anywhere on the command line",
Recursive = true)]
public string GlobalOption1 { get; set; } = "DefaultForGlobalOption1";

[CliArgument(Description = "Description for RootArgument1")]
public string RootArgument1 { get; set; }

public void Run(CliContext context)
{
context.ShowValues();
}

[CliCommand(Description = "A nested level 1 sub-command which accesses the root command")]
public class Level1SubCliCommand
{
[CliOption(
Description = "This is global for all sub commands (it can appear anywhere after the level-1 verb)",
Recursive = true)]
public string Level1RecursiveOption1 { get; set; } = "DefaultForLevel1RecusiveOption1";

[CliArgument(Description = "Description for Argument1")]
public string Argument1 { get; set; }

// The parent command gets automatically injected
public ParentCommandAccessorCliCommand RootCommand { get; set; }

public void Run(CliContext context)
{
context.ShowValues();
}

[CliCommand(Description = "A nested level 2 sub-command which accesses its parent commands")]
public class Level2SubCliCommand
{
[CliOption(Description = "Description for Option1")]
public string Option1 { get; set; } = "DefaultForOption1";

[CliArgument(Description = "Description for Argument1")]
public string Argument1 { get; set; }

// All ancestor commands gets injected
public ParentCommandAccessorCliCommand RootCommand { get; set; }
public Level1SubCliCommand ParentCommand { get; set; }

public void Run(CliContext context)
{
context.ShowValues();

Console.WriteLine();
Console.WriteLine(@$"Level1RecursiveOption1 = {ParentCommand.Level1RecursiveOption1}");
Console.WriteLine(@$"parent Argument1 = {ParentCommand.Argument1}");
Console.WriteLine(@$"GlobalOption1 = {RootCommand.GlobalOption1}");
Console.WriteLine(@$"RootArgument1 = {RootCommand.RootArgument1}");
}
}
}
}
```

### Command Inheritance

When you have repeating/common options and arguments for your commands, you can define them once in a base class and then
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<!-- https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#generateassemblyinfo -->
<VersionPrefix>1.8.6</VersionPrefix>
<VersionPrefix>1.8.7</VersionPrefix>
<Product>DotMake Command-Line</Product>
<Company>DotMake</Company>
<!-- Copyright is also used for NuGet metadata -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,18 @@ namespace DotMake.CommandLine.SourceGeneration
{
public class CliCommandAsDelegateInfo : CliSymbolInfo, IEquatable<CliCommandAsDelegateInfo>
{
private const string TaskFullName = "System.Threading.Tasks.Task";
private const string TaskIntFullName = "System.Threading.Tasks.Task<System.Int32>";
public static readonly string CliCommandAsDelegateFullName = "DotMake.CommandLine.CliCommandAsDelegate";

public CliCommandAsDelegateInfo(ISymbol symbol, SyntaxNode syntaxNode, SemanticModel semanticModel)
: base(symbol, syntaxNode, semanticModel)
{
Symbol = (IMethodSymbol)symbol;

if (Symbol.IsAsync || Symbol.ReturnType.ToCompareString() is TaskFullName or TaskIntFullName)
if (Symbol.IsAsync || Symbol.ReturnType.IsTask() || Symbol.ReturnType.IsTaskInt())
{
IsAsync = true;
ReturnsVoid = (Symbol.ReturnType.ToCompareString() == TaskFullName);
ReturnsValue = (Symbol.ReturnType is INamedTypeSymbol namedTypeSymbol)
&& namedTypeSymbol.IsGenericType
&& namedTypeSymbol.BaseType?.ToCompareString() == TaskFullName
&& (namedTypeSymbol.TypeArguments.FirstOrDefault().SpecialType == SpecialType.System_Int32);
ReturnsVoid = Symbol.ReturnType.IsTask();
ReturnsValue = Symbol.ReturnType.IsTaskInt();
}
else
{
Expand Down
21 changes: 6 additions & 15 deletions src/DotMake.CommandLine.SourceGeneration/CliCommandHandlerInfo.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
using System;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace DotMake.CommandLine.SourceGeneration
{
public class CliCommandHandlerInfo : CliSymbolInfo, IEquatable<CliCommandHandlerInfo>
{
private const string TaskFullName = "System.Threading.Tasks.Task";
private const string TaskIntFullName = "System.Threading.Tasks.Task<System.Int32>";
private const string CliContextFullName = "DotMake.CommandLine.CliContext";
public const string DiagnosticName = "CLI command handler";

Expand All @@ -17,14 +14,11 @@ public CliCommandHandlerInfo(IMethodSymbol symbol, SyntaxNode syntaxNode, Semant
Symbol = symbol;
Parent = parent;

if (symbol.IsAsync || (Symbol.Name == "RunAsync" && Symbol.ReturnType.ToCompareString() is TaskFullName or TaskIntFullName))
if (symbol.IsAsync || symbol.ReturnType.IsTask() || symbol.ReturnType.IsTaskInt())
{
IsAsync = true;
ReturnsVoid = (symbol.ReturnType.ToCompareString() == TaskFullName);
ReturnsValue = (symbol.ReturnType is INamedTypeSymbol namedTypeSymbol)
&& namedTypeSymbol.IsGenericType
&& namedTypeSymbol.BaseType?.ToCompareString() == TaskFullName
&& (namedTypeSymbol.TypeArguments.FirstOrDefault().SpecialType == SpecialType.System_Int32);
ReturnsVoid = symbol.ReturnType.IsTask();
ReturnsValue = symbol.ReturnType.IsTaskInt();
}
else
{
Expand Down Expand Up @@ -78,12 +72,9 @@ private void Analyze()

public static bool HasCorrectName(IMethodSymbol symbol)
{
return symbol.Name switch
{
"Run" => true,
"RunAsync" => symbol.IsAsync || symbol.ReturnType.ToCompareString() is TaskFullName or TaskIntFullName,
_ => false
};
return symbol.IsAsync || symbol.ReturnType.IsTask() || symbol.ReturnType.IsTaskInt()
? (symbol.Name == "RunAsync")
: (symbol.Name == "Run");
}

public void AppendCSharpCallString(CodeStringBuilder sb, string varCliContext = null)
Expand Down
15 changes: 15 additions & 0 deletions src/DotMake.CommandLine.SourceGeneration/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public static class SymbolExtensions
miscellaneousOptions:
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers);

private const string TaskFullName = "System.Threading.Tasks.Task";

/// <summary>
/// Converts the symbol to a string representation, which is suitable for calling the symbol in code.
/// <para>
Expand Down Expand Up @@ -137,5 +139,18 @@ public static string CombineNameParts(params string[] nameParts)
{
return string.Join(".", nameParts.Where(n => !string.IsNullOrEmpty(n)));
}

public static bool IsTask(this ITypeSymbol type)
{
return (type.ToCompareString() == TaskFullName);
}

public static bool IsTaskInt(this ITypeSymbol type)
{
return (type is INamedTypeSymbol namedTypeSymbol)
&& namedTypeSymbol.IsGenericType
&& namedTypeSymbol.BaseType?.ToCompareString() == TaskFullName
&& (namedTypeSymbol.TypeArguments.FirstOrDefault().SpecialType == SpecialType.System_Int32);
}
}
}
7 changes: 3 additions & 4 deletions src/DotMake.CommandLine/DotMake.CommandLine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
<Description>Declarative syntax for System.CommandLine via attributes for easy, fast, strongly-typed (no reflection) usage. Includes a source generator which automagically converts your classes to CLI commands and properties to CLI options or CLI arguments.</Description>
<PackageTags>command-line CLI console System.CommandLine declarative attributes parsing command argument option generator</PackageTags>
<PackageReleaseNotes>
- Added parent command accessor support. Sub-commands can get a reference to the parent command by adding a property of the parent command type.
- ParseResultExtensions.Bind improvement: Binding will be done only once per definition class, so calling this method consecutively for
the same definition class will return the cached result.
- Updated to latest daily build 2.0.0-beta4.24201.1 of System.CommandLine.
- Added support for command handlers or command as delegates without the async keyword (which returns Task or Task&lt;int&gt;).
</PackageReleaseNotes>
</PropertyGroup>

Expand All @@ -35,7 +34,7 @@
as it's not on official nuget feed, we directly reference the DLL inside the package so that it's also bundled in our package.
https://blog.maartenballiauw.be/post/2020/04/22/referencing-specific-assembly-nuget-package.html
-->
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.24126.1">
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.24201.1">
<GeneratePathProperty>true</GeneratePathProperty>
<IncludeAssets>None</IncludeAssets>
<ExcludeAssets>All</ExcludeAssets>
Expand Down
2 changes: 1 addition & 1 deletion src/DotMake.CommandLine/ParseResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static class ParseResultExtensions
{
private static readonly ConcurrentDictionary<ParseResult, ConcurrentDictionary<Type, object>> BindResults = new();

/// <inheritdoc cref = "Bind{TDefinition}" />
/// <inheritdoc cref = "Bind" />
/// <typeparam name="TDefinition"><inheritdoc cref="Cli.GetConfiguration{TDefinition}" path="/typeparam[@name='TDefinition']/node()" /></typeparam>
public static TDefinition Bind<TDefinition>(this ParseResult parseResult)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.6.0
// Generated by DotMake.CommandLine.SourceGeneration v1.8.7.0
// Roslyn (Microsoft.CodeAnalysis) v4.900.24.12101
// Generation: 1

Expand Down
Loading

0 comments on commit adbd9f1

Please sign in to comment.