Skip to content

Commit 917b291

Browse files
Added GetValue and other Get/TryGet methods
Also did renames that were later changed again
1 parent da0b357 commit 917b291

File tree

2 files changed

+127
-20
lines changed

2 files changed

+127
-20
lines changed

src/System.CommandLine.Subsystems/Subsystems/Annotations/ValueAnnotations.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public static class ValueAnnotations
1212
{
1313
public static string Prefix { get; } = nameof(SubsystemKind.Value);
1414

15-
public static AnnotationId<object> Explicit { get; } = new(Prefix, nameof(Explicit));
16-
public static AnnotationId<Func<ValueResult, object?>> Calculated { get; } = new(Prefix, nameof(Calculated));
15+
public static AnnotationId<object?> ExplicitDefault { get; } = new(Prefix, nameof(ExplicitDefault));
16+
public static AnnotationId<Func<object?>?> DefaultCalculation { get; } = new(Prefix, nameof(DefaultCalculation));
17+
public static AnnotationId<object?> Value { get; } = new(Prefix, nameof(Value));
1718
}

src/System.CommandLine.Subsystems/ValueSubsystem.cs

Lines changed: 124 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,141 @@ namespace System.CommandLine;
99

1010
public class ValueSubsystem : CliSubsystem
1111
{
12-
private ParseResult? parseResult = null;
12+
// @mhutch: Is the TryGet on the sparse dictionaries how we should handle a case where the annotations will be sparse to support lazy? If so, should we have another method on
13+
// the annotation wrapper, or an alternative struct when there a TryGet makes sense? This API needs review, maybe next Tuesday.
14+
private PipelineContext? pipelineContext = null;
1315

1416
public ValueSubsystem(IAnnotationProvider? annotationProvider = null)
1517
: base(ValueAnnotations.Prefix, SubsystemKind.Version, annotationProvider)
16-
{ }
18+
{ }
1719

18-
void SetExplicit(CliSymbol symbol, object value)
19-
=> SetAnnotation(symbol, ValueAnnotations.Explicit, value);
20-
object GetExplicit(CliSymbol symbol)
21-
=> TryGetAnnotation(symbol, ValueAnnotations.Explicit, out var value)
22-
? value
20+
internal void SetExplicitDefault(CliSymbol symbol, object? defaultValue)
21+
=> SetAnnotation(symbol, ValueAnnotations.ExplicitDefault, defaultValue);
22+
internal object? GetExplicitDefault(CliSymbol symbol)
23+
=> TryGetAnnotation(symbol, ValueAnnotations.ExplicitDefault, out var defaultValue)
24+
? defaultValue
2325
: "";
24-
AnnotationAccessor<object> Explicit
25-
=> new(this, ValueAnnotations.Explicit);
26+
internal bool TryGetExplicitDefault<T>(CliSymbol symbol, out T? defaultValue)
27+
{
28+
if (TryGetAnnotation(symbol, ValueAnnotations.Value, out var objectValue))
29+
{
30+
defaultValue = (T)objectValue;
31+
return true;
32+
}
33+
defaultValue = default;
34+
return false;
35+
}
36+
public AnnotationAccessor<object?> ExplicitDefault
37+
=> new(this, ValueAnnotations.ExplicitDefault);
2638

27-
void SetCalculated(CliSymbol symbol, Func<ValueResult, object?> factory)
28-
=> SetAnnotation(symbol, ValueAnnotations.Calculated, factory);
29-
Func<ValueResult, object?>? GetCalculatedValue(CliSymbol symbol)
30-
=> TryGetAnnotation<Func<ValueResult, object?>>(symbol, ValueAnnotations.Calculated, out var value)
39+
internal void SetDefaultCalculation(CliSymbol symbol, Func<object?> factory)
40+
=> SetAnnotation(symbol, ValueAnnotations.DefaultCalculation, factory);
41+
internal Func<object?>? GetDefaultCalculation(CliSymbol symbol)
42+
=> TryGetAnnotation<Func<object?>?>(symbol, ValueAnnotations.DefaultCalculation, out var value)
3143
? value
3244
: null;
45+
public AnnotationAccessor<Func<object?>?> DefaultCalculation
46+
=> new(this, ValueAnnotations.DefaultCalculation);
3347

34-
AnnotationAccessor<Func<ValueResult, object?>> Calculated
35-
=> new(this, ValueAnnotations.Calculated);
48+
private void SetValue(CliSymbol symbol, object? value)
49+
=> SetAnnotation(symbol, ValueAnnotations.Value, value);
50+
// TODO: Consider putting the logic in the generic version here
51+
// TODO: Consider using a simple dictionary instead of the annotation (@mhutch)
52+
// TODO: GetValue should call TryGetValue, not another call to TryGetAnnotation.
53+
// TODO: Should we provide an untyped value?
54+
private object? GetValue(CliSymbol symbol)
55+
=> TryGetAnnotation<object?>(symbol, ValueAnnotations.Value, out var value)
56+
? value
57+
: null;
58+
private bool TryGetValue<T>(CliSymbol symbol, out T? value)
59+
{
60+
if (TryGetAnnotation(symbol, ValueAnnotations.Value, out var objectValue))
61+
{
62+
value = (T)objectValue;
63+
return true;
64+
}
65+
value = default;
66+
return false;
67+
}
68+
// TODO: Is fluent style meaningful for Value?
69+
//public AnnotationAccessor<object?> Value
70+
// => new(this, ValueAnnotations.Value);
3671

3772
protected internal override bool GetIsActivated(ParseResult? parseResult)
73+
=> true;
74+
75+
protected internal override CliExit Execute(PipelineContext pipelineContext)
3876
{
39-
this.parseResult = parseResult;
40-
return true;
77+
this.pipelineContext = pipelineContext;
78+
return CliExit.NotRun(pipelineContext.ParseResult);
4179
}
42-
}
4380

81+
// @mhutch: I find this more readable than the if conditional version below. There will be at least two more blocks. Look good?
82+
public T? GetValue<T>(CliSymbol symbol)
83+
=> symbol switch
84+
{
85+
{ } when TryGetValue<T>(symbol, out var value)
86+
=> value, // It has already been retrieved at least once
87+
{ } when pipelineContext?.ParseResult?.GetValueResult(symbol) is ValueResult valueResult
88+
=> UseValue(symbol, valueResult.GetValue<T>()), // Value was supplied during parsing
89+
// Value was not supplied during parsing, determine default now
90+
{ } when GetDefaultCalculation(symbol) is { } defaultValueCalculation
91+
=> UseValue(symbol, CalculatedDefault<T>(symbol, defaultValueCalculation)),
92+
{ } when TryGetExplicitDefault<T>(symbol, out var explicitValue) => UseValue(symbol, explicitValue),
93+
null => throw new ArgumentNullException(nameof(symbol)),
94+
_ => UseValue(symbol, default(T))
95+
};
96+
97+
public T? GetValue2<T>(CliSymbol symbol)
98+
{
99+
if (TryGetValue<T>(symbol, out var value))
100+
{
101+
// It has already been retrieved at least once
102+
return value;
103+
}
104+
if (pipelineContext?.ParseResult?.GetValueResult(symbol) is ValueResult valueResult)
105+
{
106+
// Value was supplied during parsing
107+
return UseValue(symbol, valueResult.GetValue<T>());
108+
}
109+
// Value was not supplied during parsing, determine default now
110+
if (GetDefaultCalculation(symbol) is { } defaultValueCalculation)
111+
{
112+
return UseValue(symbol, CalculatedDefault(symbol, defaultValueCalculation));
113+
}
114+
if (TryGetExplicitDefault<T>(symbol, out var explicitValue))
115+
{
116+
return UseValue(symbol, value);
117+
}
118+
value = default;
119+
SetValue(symbol, value);
120+
return value;
121+
122+
static T? CalculatedDefault(CliSymbol symbol, Func<object?> defaultValueCalculation)
123+
{
124+
var objectValue = defaultValueCalculation();
125+
var value = objectValue is null
126+
? default
127+
: (T)objectValue;
128+
return value;
129+
}
130+
}
131+
132+
133+
private static T? CalculatedDefault<T>(CliSymbol symbol, Func<object?> defaultValueCalculation)
134+
{
135+
var objectValue = defaultValueCalculation();
136+
var value = objectValue is null
137+
? default
138+
: (T)objectValue;
139+
return value;
140+
}
141+
142+
private T? UseValue<T>(CliSymbol symbol, T? value)
143+
{
144+
SetValue(symbol, value);
145+
return value;
146+
}
147+
148+
149+
}

0 commit comments

Comments
 (0)