Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ba783e4

Browse filesBrowse files
committed
enable easier custom model binding
1 parent 25e6b36 commit ba783e4
Copy full SHA for ba783e4
Expand file treeCollapse file tree

26 files changed

+170
-231
lines changed

‎src/System.CommandLine.DragonFruit.Tests/ConfigureFromMethodTests.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine.DragonFruit.Tests/ConfigureFromMethodTests.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public void Parameters_named_arguments_generate_command_arguments_having_the_cor
162162

163163
var rootCommandArgument = parser.Configuration.RootCommand.Arguments.Single();
164164

165-
rootCommandArgument.Type
165+
rootCommandArgument.ValueType
166166
.Should()
167167
.Be(expectedType);
168168
}

‎src/System.CommandLine.DragonFruit/CommandLine.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine.DragonFruit/CommandLine.cs
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ public static IEnumerable<Option> BuildOptions(this MethodInfo type)
279279
};
280280

281281
foreach (var option in descriptor.ParameterDescriptors
282-
.Where(d => !omittedTypes.Contains (d.Type))
282+
.Where(d => !omittedTypes.Contains (d.ValueType))
283283
.Where(d => !_argumentParameterNames.Contains(d.ValueName))
284284
.Select(p => p.BuildOption()))
285285
{
@@ -291,7 +291,7 @@ public static Option BuildOption(this ParameterDescriptor parameter)
291291
{
292292
var argument = new Argument
293293
{
294-
ArgumentType = parameter.Type
294+
ArgumentType = parameter.ValueType
295295
};
296296

297297
if (parameter.HasDefaultValue)

‎src/System.CommandLine.Tests/Binding/HandlerDescriptorTests.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine.Tests/Binding/HandlerDescriptorTests.cs
+2-27Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void It_provides_the_types_of_the_handler_parameters(Type parameterType)
4141
var descriptor = HandlerDescriptor.FromMethodInfo(method);
4242

4343
descriptor.ParameterDescriptors
44-
.Select(p => p.Type)
44+
.Select(p => p.ValueType)
4545
.Should()
4646
.BeEquivalentSequenceTo(parameterType);
4747
}
@@ -76,7 +76,7 @@ public void It_provides_the_types_of_the_handler_parameters(Type parameterType)
7676
var descriptor = HandlerDescriptor.FromMethodInfo(method);
7777

7878
descriptor.ParameterDescriptors
79-
.Select(p => p.Type)
79+
.Select(p => p.ValueType)
8080
.Should()
8181
.BeEquivalentSequenceTo(parameterType);
8282
}
@@ -85,30 +85,5 @@ public void It_provides_the_types_of_the_handler_parameters(Type parameterType)
8585
public void Handler<T>(T value)
8686
{
8787
}
88-
89-
public class FromExpression
90-
{
91-
[Fact]
92-
public void Handler_descriptor_describes_the_parameter_names_of_the_handler_method()
93-
{
94-
var descriptor = HandlerDescriptor.FromExpression<ClassWithInvokeAndDefaultCtor, string, int, Task<int>>((model, s, i) => model.Invoke(s, i));
95-
96-
descriptor.ParameterDescriptors
97-
.Select(p => p.ValueName)
98-
.Should()
99-
.BeEquivalentSequenceTo("stringParam", "intParam");
100-
}
101-
102-
[Fact]
103-
public void Handler_descriptor_describes_the_parameter_types_of_the_handler_method()
104-
{
105-
var descriptor = HandlerDescriptor.FromExpression<ClassWithInvokeAndDefaultCtor, string, int, Task<int>>((model, s, i) => model.Invoke(s, i));
106-
107-
descriptor.ParameterDescriptors
108-
.Select(p => p.Type)
109-
.Should()
110-
.BeEquivalentSequenceTo(typeof(string), typeof(int));
111-
}
112-
}
11388
}
11489
}

‎src/System.CommandLine.Tests/Binding/ModelBinderTests.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine.Tests/Binding/ModelBinderTests.cs
+66-4Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.CommandLine.Binding;
5+
using System.CommandLine.Builder;
6+
using System.CommandLine.Invocation;
57
using System.CommandLine.Parsing;
68
using System.IO;
79
using FluentAssertions;
@@ -523,8 +525,10 @@ public void Explicit_model_binder_binds_only_to_configured_properties()
523525
var parser = new Parser(intOption, stringOption);
524526

525527
var bindingContext = new BindingContext(parser.Parse("--int-property 42 --string-property Hello"));
526-
var binder = new ModelBinder<ClassWithMultiLetterSetters>()
527-
{ EnforceExplicitBinding = true };
528+
var binder = new ModelBinder<ClassWithMultiLetterSetters>
529+
{
530+
EnforceExplicitBinding = true
531+
};
528532
binder.BindMemberFromValue(obj => obj.IntOption, intOption);
529533
var instance = binder.CreateInstance(bindingContext) as ClassWithMultiLetterSetters;
530534

@@ -545,14 +549,72 @@ public void Explicit_model_binder_binds_only_to_configured_ctor_parameters()
545549
var paramInfo = ctor.GetParameters().First();
546550

547551
var bindingContext = new BindingContext(parser.Parse("-a 42 -b Hello"));
548-
var binder = new ModelBinder<ClassWithMultiLetterCtorParameters>()
549-
{ EnforceExplicitBinding = true };
552+
var binder = new ModelBinder<ClassWithMultiLetterCtorParameters>
553+
{
554+
EnforceExplicitBinding = true
555+
};
550556
binder.BindConstructorArgumentFromValue(paramInfo, intOption);
551557
var instance = binder.CreateInstance(bindingContext) as ClassWithMultiLetterCtorParameters;
552558

553559
instance.Should().NotBeNull();
554560
instance.IntOption.Should().Be(42);
555561
instance.StringOption.Should().Be("the default");
556562
}
563+
564+
[Fact]
565+
public void Custom_ModelBinders_specified_via_BindingContext_can_be_used_for_option_binding()
566+
{
567+
ClassWithSetter<int> boundInstance = null;
568+
569+
var rootCommand = new RootCommand
570+
{
571+
new Option<int>("--value")
572+
};
573+
574+
rootCommand.Handler = CommandHandler.Create<ClassWithSetter<int>>(x => boundInstance = x);
575+
576+
var parser = new CommandLineBuilder(rootCommand)
577+
.UseMiddleware(context =>
578+
{
579+
var binder = new ModelBinder<ClassWithSetter<int>>();
580+
581+
binder.BindMemberFromValue(instance => instance.Value, _ => 456);
582+
583+
context.BindingContext.AddModelBinder(binder);
584+
})
585+
.Build();
586+
587+
parser.Invoke("--value 123");
588+
589+
boundInstance.Value.Should().Be(456);
590+
}
591+
592+
[Fact]
593+
public void Custom_ModelBinders_specified_via_BindingContext_can_be_used_for_command_argument_binding()
594+
{
595+
ClassWithSetter<int> boundInstance = null;
596+
597+
var rootCommand = new RootCommand
598+
{
599+
new Argument<int>()
600+
};
601+
602+
rootCommand.Handler = CommandHandler.Create<ClassWithSetter<int>>(x => boundInstance = x);
603+
604+
var parser = new CommandLineBuilder(rootCommand)
605+
.UseMiddleware(context =>
606+
{
607+
var binder = new ModelBinder<ClassWithSetter<int>>();
608+
609+
binder.BindMemberFromValue(instance => instance.Value, _ => 456);
610+
611+
context.BindingContext.AddModelBinder(binder);
612+
})
613+
.Build();
614+
615+
parser.Invoke("123");
616+
617+
boundInstance.Value.Should().Be(456);
618+
}
557619
}
558620
}

‎src/System.CommandLine.Tests/Binding/TestModels.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine.Tests/Binding/TestModels.cs
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using System;
54
using System.Threading.Tasks;
65

76
namespace System.CommandLine.Tests.Binding

‎src/System.CommandLine/Argument.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine/Argument.cs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,6 @@ public override IEnumerable<string> GetSuggestions(string textToMatch = null)
217217

218218
string IValueDescriptor.ValueName => Name;
219219

220-
Type IValueDescriptor.Type => ArgumentType;
220+
Type IValueDescriptor.ValueType => ArgumentType;
221221
}
222222
}

‎src/System.CommandLine/Binding/BindingContext.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine/Binding/BindingContext.cs
+22-20Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.Collections.Generic;
45
using System.CommandLine.Help;
56
using System.CommandLine.Invocation;
67
using System.CommandLine.IO;
78
using System.CommandLine.Parsing;
89
using System.Linq;
910

11+
#nullable enable
12+
1013
namespace System.CommandLine.Binding
1114
{
1215
public sealed class BindingContext
1316
{
1417
private IConsole _console;
18+
private readonly Dictionary<Type, ModelBinder> _modelBindersByValueDescriptor = new Dictionary<Type, ModelBinder>();
1519

1620
public BindingContext(
1721
ParseResult parseResult,
18-
IConsole console = null)
22+
IConsole? console = default)
1923
{
2024
_console = console ?? new SystemConsole();
2125

@@ -25,7 +29,7 @@ public BindingContext(
2529

2630
public ParseResult ParseResult { get; set; }
2731

28-
internal IConsoleFactory ConsoleFactory { get; set; }
32+
internal IConsoleFactory? ConsoleFactory { get; set; }
2933

3034
internal IHelpBuilder HelpBuilder => (IHelpBuilder)ServiceProvider.GetService(typeof(IHelpBuilder));
3135

@@ -46,18 +50,16 @@ public IConsole Console
4650

4751
internal ServiceProvider ServiceProvider { get; }
4852

49-
public void AddService(Type serviceType, Func<IServiceProvider, object> factory)
50-
{
51-
if (serviceType == null)
52-
{
53-
throw new ArgumentNullException(nameof(serviceType));
54-
}
53+
public void AddModelBinder(ModelBinder binder) =>
54+
_modelBindersByValueDescriptor.Add(binder.ValueDescriptor.ValueType, binder);
5555

56-
if (factory == null)
57-
{
58-
throw new ArgumentNullException(nameof(factory));
59-
}
56+
public ModelBinder GetModelBinder(IValueDescriptor valueDescriptor) =>
57+
_modelBindersByValueDescriptor.GetOrAdd(
58+
valueDescriptor.ValueType,
59+
_ => new ModelBinder(valueDescriptor));
6060

61+
public void AddService(Type serviceType, Func<IServiceProvider, object> factory)
62+
{
6163
ServiceProvider.AddService(serviceType, factory);
6264
}
6365

@@ -68,31 +70,31 @@ public void AddService<T>(Func<IServiceProvider, T> factory)
6870
throw new ArgumentNullException(nameof(factory));
6971
}
7072

71-
ServiceProvider.AddService(typeof(T), s => factory(s));
73+
ServiceProvider.AddService(typeof(T), s => factory(s)!);
7274
}
7375

7476
internal bool TryGetValueSource(
7577
IValueDescriptor valueDescriptor,
76-
out IValueSource valueSource)
78+
out IValueSource? valueSource)
7779
{
78-
if (ServiceProvider.AvailableServiceTypes.Contains(valueDescriptor.Type))
80+
if (ServiceProvider.AvailableServiceTypes.Contains(valueDescriptor.ValueType))
7981
{
8082
valueSource = new ServiceProviderValueSource();
8183
return true;
8284
}
8385

84-
valueSource = null;
86+
valueSource = default;
8587
return false;
8688
}
8789

8890
internal bool TryBindToScalarValue(
8991
IValueDescriptor valueDescriptor,
9092
IValueSource valueSource,
91-
out BoundValue boundValue)
93+
out BoundValue? boundValue)
9294
{
9395
if (valueSource.TryGetValue(valueDescriptor, this, out var value))
9496
{
95-
if (value == null || valueDescriptor.Type.IsInstanceOfType(value))
97+
if (value == null || valueDescriptor.ValueType.IsInstanceOfType(value))
9698
{
9799
boundValue = new BoundValue(value, valueDescriptor, valueSource);
98100
return true;
@@ -101,7 +103,7 @@ internal bool TryBindToScalarValue(
101103
{
102104
var parsed = ArgumentConverter.ConvertObject(
103105
valueDescriptor as IArgument ?? new Argument(valueDescriptor.ValueName),
104-
valueDescriptor.Type,
106+
valueDescriptor.ValueType,
105107
value);
106108

107109
if (parsed is SuccessfulArgumentConversionResult successful)
@@ -112,7 +114,7 @@ internal bool TryBindToScalarValue(
112114
}
113115
}
114116

115-
boundValue = null;
117+
boundValue = default;
116118
return false;
117119
}
118120
}

‎src/System.CommandLine/Binding/BoundValue.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine/Binding/BoundValue.cs
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ internal BoundValue(
1111
IValueSource valueSource)
1212
{
1313
if (value != null &&
14-
!valueDescriptor.Type.IsInstanceOfType(value))
14+
!valueDescriptor.ValueType.IsInstanceOfType(value))
1515
{
16-
throw new ArgumentException($"Value {value} ({value.GetType()}) must be an instance of type {valueDescriptor.Type}");
16+
throw new ArgumentException($"Value {value} ({value.GetType()}) must be an instance of type {valueDescriptor.ValueType}");
1717
}
1818

1919
Value = value;

‎src/System.CommandLine/Binding/DelegateHandlerDescriptor.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine/Binding/DelegateHandlerDescriptor.cs
+1-5Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ public DelegateHandlerDescriptor(Delegate handlerDelegate)
1818

1919
public override ICommandHandler GetCommandHandler()
2020
{
21-
var parameterBinders = ParameterDescriptors
22-
.Select(p => new ModelBinder(p))
23-
.ToList();
24-
2521
return new ModelBindingCommandHandler(
2622
_handlerDelegate,
27-
parameterBinders);
23+
ParameterDescriptors);
2824
}
2925

3026
public override ModelDescriptor Parent => null;

‎src/System.CommandLine/Binding/ExpressionHandlerDescriptor.cs

Copy file name to clipboardExpand all lines: src/System.CommandLine/Binding/ExpressionHandlerDescriptor.cs
-60Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.