Skip to content

Commit 3e225df

Browse files
authored
Merge pull request #777 from jonsequitur/correct-parent-symbol-on-implicit-argument
set parent symbol when custom parse delegate is called on default value
2 parents 6274e0a + 72dbf46 commit 3e225df

File tree

5 files changed

+110
-13
lines changed

5 files changed

+110
-13
lines changed

src/System.CommandLine.Tests/ArgumentTests.cs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public void GetDefaultValue_can_return_null()
117117
}
118118

119119
[Fact]
120-
public void validation_failure_message()
120+
public void Validation_failure_message_can_be_specified()
121121
{
122122
var argument = new Argument<FileSystemInfo>(result =>
123123
{
@@ -188,6 +188,79 @@ public void custom_parsing_of_scalar_value_from_an_argument_with_multiple_tokens
188188
.Should()
189189
.Be(6);
190190
}
191+
192+
[Fact]
193+
public void ArgumentResult_Parent_is_set_correctly_when_token_is_present()
194+
{
195+
ArgumentResult argumentResult = null;
196+
197+
var command = new Command("the-command")
198+
{
199+
new Option<string>(
200+
"-x",
201+
parseArgument: argResult =>
202+
{
203+
argumentResult = argResult;
204+
return null;
205+
})
206+
};
207+
208+
command.Parse("-x abc");
209+
210+
argumentResult
211+
.Parent
212+
.Should()
213+
.NotBeNull();
214+
}
215+
216+
[Fact]
217+
public void Option_ArgumentResult_Parent_is_set_correctly_when_token_is_implicit()
218+
{
219+
ArgumentResult argumentResult = null;
220+
221+
var command = new Command("the-command")
222+
{
223+
new Option<string>(
224+
"-x",
225+
parseArgument: argResult =>
226+
{
227+
argumentResult = argResult;
228+
return null;
229+
}, isDefault: true)
230+
};
231+
232+
command.Parse("");
233+
234+
argumentResult
235+
.Parent
236+
.Symbol
237+
.Should()
238+
.Be(command.Options.Single());
239+
}
240+
241+
[Fact]
242+
public void Command_ArgumentResult_Parent_is_set_correctly_when_token_is_implicit()
243+
{
244+
ArgumentResult argumentResult = null;
245+
246+
var command = new Command("the-command")
247+
{
248+
new Argument<string>(
249+
parse: argResult =>
250+
{
251+
argumentResult = argResult;
252+
return null;
253+
}, isDefault: true)
254+
};
255+
256+
command.Parse("");
257+
258+
argumentResult
259+
.Parent
260+
.Symbol
261+
.Should()
262+
.Be(command);
263+
}
191264
}
192265

193266
protected override Symbol CreateSymbol(string name)

src/System.CommandLine/Argument.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace System.CommandLine
1111
{
1212
public class Argument : Symbol, IArgument
1313
{
14-
private Func<object> _defaultValueFactory;
14+
private Func<ArgumentResult, object> _defaultValueFactory;
1515
private readonly List<string> _suggestions = new List<string>();
1616
private readonly List<ISuggestionSource> _suggestionSources = new List<ISuggestionSource>();
1717
private IArgumentArity _arity;
@@ -115,13 +115,18 @@ bool DefaultConvert(SymbolResult symbol, out object value)
115115
public void AddValidator(ValidateSymbol<ArgumentResult> validator) => Validators.Add(validator);
116116

117117
public object GetDefaultValue()
118+
{
119+
return GetDefaultValue(new ArgumentResult(this, null));
120+
}
121+
122+
internal object GetDefaultValue(ArgumentResult argumentResult)
118123
{
119124
if (_defaultValueFactory is null)
120125
{
121126
throw new InvalidOperationException($"Argument \"{Name}\" does not have a default value");
122127
}
123128

124-
return _defaultValueFactory.Invoke();
129+
return _defaultValueFactory.Invoke(argumentResult);
125130
}
126131

127132
public void SetDefaultValue(object value)
@@ -130,6 +135,16 @@ public void SetDefaultValue(object value)
130135
}
131136

132137
public void SetDefaultValueFactory(Func<object> getDefaultValue)
138+
{
139+
if (getDefaultValue == null)
140+
{
141+
throw new ArgumentNullException(nameof(getDefaultValue));
142+
}
143+
144+
SetDefaultValueFactory(_ => getDefaultValue());
145+
}
146+
147+
public void SetDefaultValueFactory(Func<ArgumentResult, object> getDefaultValue)
133148
{
134149
_defaultValueFactory = getDefaultValue ?? throw new ArgumentNullException(nameof(getDefaultValue));
135150
}

src/System.CommandLine/Argument{T}.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,7 @@ public Argument(ParseArgument<T> parse, bool isDefault = false) : this()
7474

7575
if (isDefault)
7676
{
77-
SetDefaultValueFactory(() =>
78-
{
79-
var argumentResult = new ArgumentResult(
80-
this,
81-
null);
82-
83-
return parse(argumentResult);
84-
});
77+
SetDefaultValueFactory(argumentResult => parse(argumentResult));
8578
}
8679

8780
ConvertArguments = (ArgumentResult argumentResult, out object value) =>

src/System.CommandLine/Parsing/ArgumentResult.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,8 @@ bool ShouldCheckArity()
102102
optionResult.IsImplicit);
103103
}
104104
}
105+
106+
internal override object CreateDefaultArgumentResultAndGetItsValue(Argument argument) =>
107+
argument.GetDefaultValue(this);
105108
}
106109
}

src/System.CommandLine/Parsing/SymbolResult.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.Collections.Generic;
5+
using System.CommandLine.Binding;
56
using System.Linq;
67

78
namespace System.CommandLine.Parsing
@@ -63,11 +64,23 @@ protected internal ValidationMessages ValidationMessages
6364

6465
internal void AddToken(Token token) => _tokens.Add(token);
6566

66-
internal object GetDefaultValueFor(IArgument argument)
67+
internal virtual object GetDefaultValueFor(IArgument argument)
6768
{
6869
return _defaultArgumentValues.GetOrAdd(
6970
argument,
70-
a => a.GetDefaultValue());
71+
a => a is Argument arg
72+
? CreateDefaultArgumentResultAndGetItsValue(arg)
73+
: a.GetDefaultValue());
74+
}
75+
76+
internal virtual object CreateDefaultArgumentResultAndGetItsValue(Argument argument)
77+
{
78+
if (!(Children.ResultFor(argument) is ArgumentResult result))
79+
{
80+
result = new ArgumentResult(argument, this);
81+
}
82+
83+
return argument.GetDefaultValue(result);
7184
}
7285

7386
internal bool UseDefaultValueFor(IArgument argument)

0 commit comments

Comments
 (0)