mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Cleanup
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<CollectCoverage>true</CollectCoverage>
|
||||
<CoverletOutputFormat>opencover</CoverletOutputFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<CollectCoverage>true</CollectCoverage>
|
||||
<CoverletOutputFormat>opencover</CoverletOutputFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -496,6 +496,83 @@ public class Command : ICommand
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Option_binding_supports_multiple_inheritance_through_default_interface_members()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
@"
|
||||
public static class SharedContext
|
||||
{
|
||||
public static int Foo { get; set; }
|
||||
|
||||
public static bool Bar { get; set; }
|
||||
}
|
||||
|
||||
public interface IHasFoo : ICommand
|
||||
{
|
||||
[CommandOption(""foo"")]
|
||||
public int Foo
|
||||
{
|
||||
get => SharedContext.Foo;
|
||||
set => SharedContext.Foo = value;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IHasBar : ICommand
|
||||
{
|
||||
[CommandOption(""bar"")]
|
||||
public bool Bar
|
||||
{
|
||||
get => SharedContext.Bar;
|
||||
set => SharedContext.Bar = value;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IHasBaz : ICommand
|
||||
{
|
||||
public string Baz { get; set; }
|
||||
}
|
||||
|
||||
[Command]
|
||||
public class Command : IHasFoo, IHasBar, IHasBaz
|
||||
{
|
||||
[CommandOption(""baz"")]
|
||||
public string Baz { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine(""Foo = "" + SharedContext.Foo);
|
||||
console.Output.WriteLine(""Bar = "" + SharedContext.Bar);
|
||||
console.Output.WriteLine(""Baz = "" + Baz);
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand(commandType)
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] { "--foo", "42", "--bar", "--baz", "xyz" }
|
||||
);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = 42",
|
||||
"Bar = True",
|
||||
"Baz = xyz"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Option_binding_does_not_consider_a_negative_number_as_an_option_name_or_short_name()
|
||||
{
|
||||
@@ -704,64 +781,4 @@ public class Command : ICommand
|
||||
exitCode.Should().NotBe(0);
|
||||
stdErr.Should().Contain("expects a single argument, but provided with multiple");
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Option_bound_using_interfaces_for_multiple_inheritance_should_work()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
@"
|
||||
public static class FooBarLogger
|
||||
{
|
||||
public static bool Foo { get; set; } = false;
|
||||
public static bool Bar { get; set; } = false;
|
||||
}
|
||||
public interface IOptionBar : ICommand
|
||||
{
|
||||
[CommandOption(""bar"")]
|
||||
public bool Bar
|
||||
{
|
||||
get => FooBarLogger.Bar;
|
||||
set => FooBarLogger.Bar = value;
|
||||
}
|
||||
}
|
||||
public interface IOptionFoo : ICommand
|
||||
{
|
||||
[CommandOption(""foo"")]
|
||||
public bool Foo
|
||||
{
|
||||
get => FooBarLogger.Foo;
|
||||
set => FooBarLogger.Foo = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Command]
|
||||
public class Command : IOptionFoo, IOptionBar
|
||||
{
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine($""Foo: { FooBarLogger.Foo }"");
|
||||
console.Output.WriteLine($""Bar: { FooBarLogger.Bar }"");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand(commandType)
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo" , "--bar"});
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
stdOut.Trim().Should().Be("Foo: True" + Environment.NewLine + "Bar: True");
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,22 @@ internal partial class CommandSchema
|
||||
var implicitOptionSchemas = string.IsNullOrWhiteSpace(name)
|
||||
? new[] {OptionSchema.HelpOption, OptionSchema.VersionOption}
|
||||
: new[] {OptionSchema.HelpOption};
|
||||
|
||||
// Include interface members for multiple inheritance
|
||||
// If interface inherits from ICommand, it will be included
|
||||
var interfaces = type.GetInterfaces().Where(i => i != typeof(ICommand) && typeof(ICommand).IsAssignableFrom(i) );
|
||||
var properties = type.GetProperties().Concat(interfaces.SelectMany(i => i.GetProperties())).ToArray();
|
||||
|
||||
|
||||
var properties = type
|
||||
// Get properties directly on command type
|
||||
.GetProperties()
|
||||
// Get non-abstract properties on interfaces (to support default interfaces members)
|
||||
.Union(type
|
||||
.GetInterfaces()
|
||||
// Only interfaces implementing ICommand for explicitness
|
||||
.Where(i => typeof(ICommand).IsAssignableFrom(i) && i != typeof(ICommand))
|
||||
.SelectMany(i => i
|
||||
.GetProperties()
|
||||
.Where(p => !p.GetMethod.IsAbstract && !p.SetMethod.IsAbstract)
|
||||
)
|
||||
)
|
||||
.ToArray();
|
||||
|
||||
var parameterSchemas = properties
|
||||
.Select(ParameterSchema.TryResolve)
|
||||
.WhereNotNull()
|
||||
|
||||
Reference in New Issue
Block a user