7
7
8
8
namespace System . CommandLine ;
9
9
10
- public class Pipeline
10
+ public partial class Pipeline
11
11
{
12
- //TODO: When we allow adding subsystems, this code will change
13
- private IEnumerable < CliSubsystem ? > Subsystems
14
- => [ Help , Version , Completion , Diagram , Value , ErrorReporting ] ;
12
+ private readonly PipelinePhase < DiagramSubsystem > diagramPhase = new ( SubsystemKind . Diagram ) ;
13
+ private readonly PipelinePhase < CompletionSubsystem > completionPhase = new ( SubsystemKind . Completion ) ;
14
+ private readonly PipelinePhase < HelpSubsystem > helpPhase = new ( SubsystemKind . Help ) ;
15
+ private readonly PipelinePhase < VersionSubsystem > versionPhase = new ( SubsystemKind . Version ) ;
16
+ private readonly PipelinePhase < ValidationSubsystem > validationPhase = new ( SubsystemKind . Validation ) ;
17
+ private readonly PipelinePhase < InvocationSubsystem > invocationPhase = new ( SubsystemKind . Invocation ) ;
18
+ private readonly PipelinePhase < ErrorReportingSubsystem > errorReportingPhase = new ( SubsystemKind . ErrorReporting ) ;
19
+ private readonly IEnumerable < PipelinePhase > phases ;
15
20
21
+ /// <summary>
22
+ /// Creates an instance of the pipeline using standard features.
23
+ /// </summary>
24
+ /// <param name="help">A help subsystem to replace the standard one. To add a subsystem, use <see cref="AddSubsystem"></param>
25
+ /// <param name="version">A help subsystem to replace the standard one. To add a subsystem, use <see cref="AddSubsystem"></param>
26
+ /// <param name="completion">A help subsystem to replace the standard one. To add a subsystem, use <see cref="AddSubsystem"></param>
27
+ /// <param name="diagram">A help subsystem to replace the standard one. To add a subsystem, use <see cref="AddSubsystem"></param>
28
+ /// <param name="errorReporting">A help subsystem to replace the standard one. To add a subsystem, use <see cref="AddSubsystem"></param>
29
+ /// <returns>A new pipeline.</returns>
30
+ /// <remarks>
31
+ /// Currently, the standard <see cref="ValueSubsystem"/>, <see cref="ValidationSubsystem"/> , and <see cref="ResponseSubsystem"/> cannot be replaced. <see cref="ResponseSubsystem"/> is disabled by default.
32
+ /// </remarks>
16
33
public static Pipeline Create ( HelpSubsystem ? help = null ,
17
34
VersionSubsystem ? version = null ,
18
35
CompletionSubsystem ? completion = null ,
19
36
DiagramSubsystem ? diagram = null ,
20
- ErrorReportingSubsystem ? errorReporting = null ,
21
- ValueSubsystem ? value = null )
37
+ ErrorReportingSubsystem ? errorReporting = null )
22
38
=> new ( )
23
39
{
24
40
Help = help ?? new HelpSubsystem ( ) ,
25
41
Version = version ?? new VersionSubsystem ( ) ,
26
42
Completion = completion ?? new CompletionSubsystem ( ) ,
27
43
Diagram = diagram ?? new DiagramSubsystem ( ) ,
28
44
ErrorReporting = errorReporting ?? new ErrorReportingSubsystem ( ) ,
29
- Value = value ?? new ValueSubsystem ( )
30
45
} ;
31
46
47
+ /// <summary>
48
+ /// Creates an instance of the pipeline with no features. Use this if you want to explicitly add features.
49
+ /// </summary>
50
+ /// <returns>A new pipeline.</returns>
51
+ /// <remarks>
52
+ /// The <cref>ValueSubsystem</cref> and <see cref="ResponseSubsystem"/> is always added and cannot be changed.
53
+ /// </remarks>
32
54
public static Pipeline CreateEmpty ( )
33
55
=> new ( ) ;
34
56
35
- private Pipeline ( ) { }
57
+ private Pipeline ( )
58
+ {
59
+ Value = new ValueSubsystem ( ) ;
60
+ Response = new ResponseSubsystem ( ) ;
61
+ Invocation = new InvocationSubsystem ( ) ;
62
+ Validation = new ValidationSubsystem ( ) ;
63
+
64
+ // This order is based on: if the user entered both, which should they get?
65
+ // * It is reasonable to diagram help and completion. More reasonable than getting help on Diagram or Completion
66
+ // * A future version of Help and Version may take arguments/options. In that case, help on version is reasonable.
67
+ phases =
68
+ [
69
+ diagramPhase , completionPhase , helpPhase , versionPhase ,
70
+ validationPhase , invocationPhase , errorReportingPhase
71
+ ] ;
72
+ }
73
+
74
+ /// <summary>
75
+ /// Enables response files. They are disabled by default.
76
+ /// </summary>
77
+ public bool ResponseFilesEnabled
78
+ {
79
+ get => Response . Enabled ;
80
+ set => Response . Enabled = value ;
81
+ }
82
+
83
+ /// <summary>
84
+ /// Adds a subsystem.
85
+ /// </summary>
86
+ /// <param name="subsystem">The subsystem to add.</param>
87
+ /// <param name="timing"><see cref="PhaseTiming.Before"/> indicates that the subsystem should run before all other subsystems in the phase, and <see cref="PhaseTiming.After"/> indicates it should run after other subsystems. The default is <see cref="PhaseTiming.Before"/>.</param>
88
+ /// <exception cref="InvalidOperationException"></exception>
89
+ /// <remarks>
90
+ /// The phase in which the subsystem runs is determined by the subsystem's 'Kind' property.
91
+ /// <br/>
92
+ /// To replace one of the standard subsystems, use the `Pipeline.(subsystem)` property, such as `myPipeline.Help = new OtherHelp();`
93
+ /// </remarks>
94
+ public void AddSubsystem ( CliSubsystem subsystem , AddToPhaseBehavior timing = AddToPhaseBehavior . SubsystemRecommendation )
95
+ {
96
+ switch ( subsystem . Kind )
97
+ {
98
+ case SubsystemKind . Other :
99
+ case SubsystemKind . Response :
100
+ case SubsystemKind . Value :
101
+ throw new InvalidOperationException ( $ "You cannot add subsystems to { subsystem . Kind } ") ;
102
+ case SubsystemKind . Diagram :
103
+ diagramPhase . AddSubsystem ( subsystem , timing ) ;
104
+ break ;
105
+ case SubsystemKind . Completion :
106
+ completionPhase . AddSubsystem ( subsystem , timing ) ;
107
+ break ;
108
+ case SubsystemKind . Help :
109
+ helpPhase . AddSubsystem ( subsystem , timing ) ;
110
+ break ;
111
+ case SubsystemKind . Version :
112
+ versionPhase . AddSubsystem ( subsystem , timing ) ;
113
+ break ;
114
+ // You can add Validation and Invocation subsystems, but you can't remove the core.
115
+ // Other things may need to be run in the phase.
116
+ case SubsystemKind . Validation :
117
+ validationPhase . AddSubsystem ( subsystem , timing ) ;
118
+ break ;
119
+ case SubsystemKind . Invocation :
120
+ invocationPhase . AddSubsystem ( subsystem , timing ) ;
121
+ break ;
122
+ case SubsystemKind . ErrorReporting :
123
+ errorReportingPhase . AddSubsystem ( subsystem , timing ) ;
124
+ break ;
125
+ }
126
+ }
127
+
128
+ /// <summary>
129
+ /// Sets or gets the diagramming subsystem.
130
+ /// </summary>
131
+ public DiagramSubsystem ? Diagram
132
+ {
133
+ get => diagramPhase . Subsystem ;
134
+ set => diagramPhase . Subsystem = value ;
135
+ }
36
136
37
- public HelpSubsystem ? Help { get ; set ; }
38
- public VersionSubsystem ? Version { get ; set ; }
39
- public CompletionSubsystem ? Completion { get ; set ; }
40
- public DiagramSubsystem ? Diagram { get ; set ; }
41
- public ErrorReportingSubsystem ? ErrorReporting { get ; set ; }
42
- public ValueSubsystem ? Value { get ; set ; }
137
+ /// <summary>
138
+ /// Sets or gets the completion subsystem.
139
+ /// </summary>
140
+ public CompletionSubsystem ? Completion
141
+ {
142
+ get => completionPhase . Subsystem ;
143
+ set => completionPhase . Subsystem = value ;
144
+ }
145
+
146
+ /// <summary>
147
+ /// Sets or gets the help subsystem.
148
+ /// </summary>
149
+ public HelpSubsystem ? Help
150
+ {
151
+ get => helpPhase . Subsystem ;
152
+ set => helpPhase . Subsystem = value ;
153
+ }
154
+
155
+ /// <summary>
156
+ /// Sets or gets the version subsystem.
157
+ /// </summary>
158
+ public VersionSubsystem ? Version
159
+ {
160
+ get => versionPhase . Subsystem ;
161
+ set => versionPhase . Subsystem = value ;
162
+ }
163
+
164
+ /// <summary>
165
+ /// Sets or gets the error reporting subsystem.
166
+ /// </summary>
167
+ public ErrorReportingSubsystem ? ErrorReporting
168
+ {
169
+ get => errorReportingPhase . Subsystem ;
170
+ set => errorReportingPhase . Subsystem = value ;
171
+ }
172
+
173
+ // TODO: Consider whether replacing the validation subsystem is valuable
174
+ /// <summary>
175
+ /// Sets or gets the validation subsystem
176
+ /// </summary>
177
+ public ValidationSubsystem ? Validation { get ; }
178
+
179
+ // TODO: Consider whether replacing the invocation subsystem is valuable
180
+ /// <summary>
181
+ /// Sets or gets the invocation subsystem
182
+ /// </summary>
183
+ public InvocationSubsystem ? Invocation { get ; }
184
+
185
+ /// <summary>
186
+ /// Gets the value subsystem which manages entered and default values.
187
+ /// </summary>
188
+ public ValueSubsystem Value { get ; }
189
+
190
+ /// <summary>
191
+ /// Gets the response file subsystem
192
+ /// </summary>
193
+ public ResponseSubsystem Response { get ; }
43
194
44
195
public ParseResult Parse ( CliConfiguration configuration , string rawInput )
45
196
=> Parse ( configuration , CliParser . SplitCommandLine ( rawInput ) . ToArray ( ) ) ;
@@ -55,16 +206,24 @@ public PipelineResult Execute(CliConfiguration configuration, string rawInput, C
55
206
=> Execute ( configuration , CliParser . SplitCommandLine ( rawInput ) . ToArray ( ) , rawInput , consoleHack ) ;
56
207
57
208
public PipelineResult Execute ( CliConfiguration configuration , string [ ] args , string rawInput , ConsoleHack ? consoleHack = null )
58
- {
59
- var pipelineResult = Execute ( Parse ( configuration , args ) , rawInput , consoleHack ) ;
60
- TearDownSubsystems ( pipelineResult ) ;
61
- return pipelineResult ;
62
- }
209
+ => Execute ( Parse ( configuration , args ) , rawInput , consoleHack ) ;
63
210
64
211
public PipelineResult Execute ( ParseResult parseResult , string rawInput , ConsoleHack ? consoleHack = null )
65
212
{
66
213
var pipelineResult = new PipelineResult ( parseResult , rawInput , this , consoleHack ?? new ConsoleHack ( ) ) ;
67
- ExecuteSubsystems ( pipelineResult ) ;
214
+ foreach ( var phase in phases )
215
+ {
216
+ // TODO: Allow subsystems to control short-circuiting
217
+ foreach ( var subsystem in phase . GetSubsystems ( ) )
218
+ {
219
+ // TODO: RunEvenIfAlreadyHandled needs more thought and laying out the scenarios
220
+ if ( subsystem is not null && ( ! pipelineResult . AlreadyHandled || subsystem . RunsEvenIfAlreadyHandled ) )
221
+ {
222
+ subsystem . ExecuteIfNeeded ( pipelineResult ) ;
223
+ }
224
+ }
225
+ }
226
+ TearDownSubsystems ( pipelineResult ) ;
68
227
return pipelineResult ;
69
228
}
70
229
@@ -81,9 +240,10 @@ public PipelineResult Execute(ParseResult parseResult, string rawInput, ConsoleH
81
240
/// </remarks>
82
241
protected virtual void InitializeSubsystems ( InitializationContext context )
83
242
{
84
- foreach ( var subsystem in Subsystems )
243
+ foreach ( var phase in phases )
85
244
{
86
- if ( subsystem is not null )
245
+ // TODO: Allow subsystems to control short-circuiting? Not sure we need that for initialization
246
+ foreach ( var subsystem in phase . GetSubsystems ( ) )
87
247
{
88
248
subsystem . Initialize ( context ) ;
89
249
}
@@ -99,35 +259,13 @@ protected virtual void InitializeSubsystems(InitializationContext context)
99
259
protected virtual void TearDownSubsystems ( PipelineResult pipelineResult )
100
260
{
101
261
// TODO: Work on this design as the last pipelineResult wins and they may not all be well behaved
102
- var subsystems = Subsystems . Reverse ( ) ;
103
- foreach ( var subsystem in subsystems )
262
+ foreach ( var phase in phases )
104
263
{
105
- if ( subsystem is not null )
264
+ // TODO: Allow subsystems to control short-circuiting? Not sure we need that for teardown
265
+ foreach ( var subsystem in phase . GetSubsystems ( ) )
106
266
{
107
267
subsystem . TearDown ( pipelineResult ) ;
108
268
}
109
269
}
110
270
}
111
-
112
- protected virtual void ExecuteSubsystems ( PipelineResult pipelineResult )
113
- {
114
- // TODO: Consider redesign where pipelineResult is not modifiable.
115
- //
116
- foreach ( var subsystem in Subsystems )
117
- {
118
- if ( subsystem is not null )
119
- {
120
- subsystem . ExecuteIfNeeded ( pipelineResult ) ;
121
- }
122
- }
123
- }
124
-
125
- protected static void ExecuteIfNeeded ( CliSubsystem ? subsystem , PipelineResult pipelineResult )
126
- {
127
- if ( subsystem is not null && ( ! pipelineResult . AlreadyHandled || subsystem . RunsEvenIfAlreadyHandled ) )
128
- {
129
- subsystem . ExecuteIfNeeded ( pipelineResult ) ;
130
- }
131
- }
132
-
133
271
}
0 commit comments