@@ -9,35 +9,141 @@ namespace System.CommandLine;
9
9
10
10
public class ValueSubsystem : CliSubsystem
11
11
{
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 ;
13
15
14
16
public ValueSubsystem ( IAnnotationProvider ? annotationProvider = null )
15
17
: base ( ValueAnnotations . Prefix , SubsystemKind . Version , annotationProvider )
16
- { }
18
+ { }
17
19
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
23
25
: "" ;
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 ) ;
26
38
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 )
31
43
? value
32
44
: null ;
45
+ public AnnotationAccessor < Func < object ? > ? > DefaultCalculation
46
+ => new ( this , ValueAnnotations . DefaultCalculation ) ;
33
47
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);
36
71
37
72
protected internal override bool GetIsActivated ( ParseResult ? parseResult )
73
+ => true ;
74
+
75
+ protected internal override CliExit Execute ( PipelineContext pipelineContext )
38
76
{
39
- this . parseResult = parseResult ;
40
- return true ;
77
+ this . pipelineContext = pipelineContext ;
78
+ return CliExit . NotRun ( pipelineContext . ParseResult ) ;
41
79
}
42
- }
43
80
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