Skip to content

Commit b883f9f

Browse files
Make PipelinePhase generic
This avoids a type check and moves a runtime error to compiler time for setting the main phase subsystem, which must be of the correct type r null. Currently base and generic are in the same file to facilitate review (not having all the code show up as new in commits)
1 parent 336f568 commit b883f9f

File tree

2 files changed

+64
-205
lines changed

2 files changed

+64
-205
lines changed

src/System.CommandLine.Subsystems/Pipeline.cs

Lines changed: 23 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ namespace System.CommandLine;
1010
public partial class Pipeline
1111
{
1212
// TODO: Consider more phases that have obvious meanings, like first and last
13-
private PipelinePhase diagramPhase = new(SubsystemKind.Diagram);
14-
private PipelinePhase completionPhase = new(SubsystemKind.Completion);
15-
private PipelinePhase helpPhase = new(SubsystemKind.Help);
16-
private PipelinePhase versionPhase = new(SubsystemKind.Version);
17-
private PipelinePhase validationPhase = new(SubsystemKind.Validation);
18-
private PipelinePhase invocationPhase = new(SubsystemKind.Invocation);
19-
private PipelinePhase errorReportingPhase = new(SubsystemKind.ErrorReporting);
13+
private PipelinePhase<DiagramSubsystem> diagramPhase = new(SubsystemKind.Diagram);
14+
private PipelinePhase<CompletionSubsystem> completionPhase = new(SubsystemKind.Completion);
15+
private PipelinePhase<HelpSubsystem> helpPhase = new(SubsystemKind.Help);
16+
private PipelinePhase<VersionSubsystem> versionPhase = new(SubsystemKind.Version);
17+
private PipelinePhase<ValidationSubsystem> validationPhase = new(SubsystemKind.Validation);
18+
private PipelinePhase<InvocationSubsystem> invocationPhase = new(SubsystemKind.Invocation);
19+
private PipelinePhase<ErrorReportingSubsystem> errorReportingPhase = new(SubsystemKind.ErrorReporting);
2020
// TODO: Consider this naming as it sounds like it is a finishing phase
2121
private readonly IEnumerable<PipelinePhase> phases = [];
2222

@@ -129,107 +129,55 @@ public void AddSubsystem(CliSubsystem subsystem, PhaseTiming timing = PhaseTimin
129129
/// </summary>
130130
public DiagramSubsystem? Diagram
131131
{
132-
get
133-
=> diagramPhase.Subsystem switch
134-
{
135-
null => null,
136-
DiagramSubsystem diagramSubsystem => diagramSubsystem,
137-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
138-
};
139-
set
140-
{
141-
diagramPhase.Subsystem = value;
142-
}
132+
get => diagramPhase.Subsystem;
133+
set => diagramPhase.Subsystem = value;
143134
}
144135

145136
/// <summary>
146137
/// Sets or gets the completion subsystem.
147138
/// </summary>
148139
public CompletionSubsystem? Completion
149140
{
150-
get
151-
=> completionPhase.Subsystem switch
152-
{
153-
null => null,
154-
CompletionSubsystem completionSubsystem => completionSubsystem,
155-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
156-
};
157-
set
158-
{
159-
completionPhase.Subsystem = value;
160-
}
141+
get => completionPhase.Subsystem;
142+
set => completionPhase.Subsystem = value;
143+
161144
}
162145

163146
/// <summary>
164147
/// Sets or gets the help subsystem.
165148
/// </summary>
166149
public HelpSubsystem? Help
167150
{
168-
get
169-
=> helpPhase.Subsystem switch
170-
{
171-
null => null,
172-
HelpSubsystem helpSubsystem => helpSubsystem,
173-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
174-
};
175-
set
176-
{
177-
helpPhase.Subsystem = value;
178-
}
151+
get => helpPhase.Subsystem;
152+
set => helpPhase.Subsystem = value;
153+
179154
}
180155

181156
/// <summary>
182157
/// Sets or gets the version subsystem.
183158
/// </summary>
184159
public VersionSubsystem? Version
185160
{
186-
get
187-
=> versionPhase.Subsystem switch
188-
{
189-
null => null,
190-
VersionSubsystem versionSubsystem => versionSubsystem,
191-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
192-
};
193-
set
194-
{
195-
versionPhase.Subsystem = value;
196-
}
161+
get => versionPhase.Subsystem;
162+
set => versionPhase.Subsystem = value;
197163
}
198164

199165
/// <summary>
200166
/// Sets or gets the error reporting subsystem.
201167
/// </summary>
202168
public ErrorReportingSubsystem? ErrorReporting
203169
{
204-
get
205-
=> errorReportingPhase.Subsystem switch
206-
{
207-
null => null,
208-
ErrorReportingSubsystem errorReportingSubsystem => errorReportingSubsystem,
209-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
210-
};
211-
set
212-
{
213-
errorReportingPhase.Subsystem = value;
214-
}
170+
get => errorReportingPhase.Subsystem;
171+
set => errorReportingPhase.Subsystem = value;
215172
}
216173

217174
/// <summary>
218175
/// Sets or gets the validation subsystem
219176
/// </summary>
220177
public ValidationSubsystem? Validation
221178
{
222-
get
223-
=> validationPhase.Subsystem switch
224-
{
225-
null => null,
226-
ValidationSubsystem validationSubsystem => validationSubsystem,
227-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
228-
};
229-
set
230-
{
231-
validationPhase.Subsystem = value;
232-
}
179+
get => validationPhase.Subsystem;
180+
set => validationPhase.Subsystem = value;
233181
}
234182

235183

@@ -238,17 +186,8 @@ public ValidationSubsystem? Validation
238186
/// </summary>
239187
public InvocationSubsystem? Invocation
240188
{
241-
get
242-
=> invocationPhase.Subsystem switch
243-
{
244-
null => null,
245-
InvocationSubsystem invocationSubsystem => invocationSubsystem,
246-
_ => throw new InvalidOperationException("Version subsystem is not of the correct type")
247-
};
248-
set
249-
{
250-
invocationPhase.Subsystem = value;
251-
}
189+
get => invocationPhase.Subsystem;
190+
set => invocationPhase.Subsystem = value;
252191
}
253192

254193

src/System.CommandLine.Subsystems/Subsystems/PipelinePhase.cs

Lines changed: 41 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,22 @@
33

44
namespace System.CommandLine.Subsystems;
55

6-
/// <summary>
7-
/// This struct manages one phase. The most common case is that it is empty, and the most complicated
8-
/// case of several items before, and several items after will be quite rare.
9-
/// </summary>
10-
/// <remarks>
11-
/// <para>
12-
/// The most common case is that it is empty, and the most complicated
13-
/// case of several items before, and several items after will be quite rare. <br/>
14-
/// </para>
15-
/// <para>
16-
/// In the current design, this needs to be a reference type so values are synced.
17-
/// </para>
18-
/// </remarks>
196
internal class PipelinePhase(SubsystemKind kind)
207
{
218
private List<CliSubsystem>? before = null;
229
private List<CliSubsystem>? after = null;
2310

2411
public readonly SubsystemKind Kind = kind;
25-
26-
internal CliSubsystem? Subsystem { get; set; }
27-
12+
protected CliSubsystem? CliSubsystem { get; set; }
13+
14+
/// <summary>
15+
/// Add a subsystem to the phase
16+
/// </summary>
17+
/// <param name="subsystem">The subsystem to add</param>
18+
/// <param name="timing">Whether it should run before or after the key subsystem</param>
19+
/// <remarks>
20+
/// Adding a subsystem that is not of the normal phase type is expected and OK.
21+
/// </remarks>
2822
public void AddSubsystem(CliSubsystem subsystem, PhaseTiming timing = PhaseTiming.Before)
2923
{
3024
List<CliSubsystem>? addToList = timing == PhaseTiming.Before
@@ -48,9 +42,9 @@ private List<CliSubsystem> CreateAfterIfNeeded()
4842

4943
public IEnumerable<CliSubsystem> GetSubsystems()
5044
{
51-
List<CliSubsystem> ret = Subsystem is null
45+
List<CliSubsystem> ret = CliSubsystem is null
5246
? []
53-
: [Subsystem];
47+
: [CliSubsystem];
5448
if (before is not null)
5549
{
5650
// TODO: Confirm that we want to reverse the before list.
@@ -64,108 +58,34 @@ public IEnumerable<CliSubsystem> GetSubsystems()
6458
}
6559
}
6660

61+
/// <summary>
62+
/// This struct manages one phase. The most common case is that it is empty, and the most complicated
63+
/// case of several items before, and several items after will be quite rare.
64+
/// </summary>
65+
/// <remarks>
66+
/// <para>
67+
/// The most common case is that it is empty, and the most complicated
68+
/// case of several items before, and several items after will be quite rare. <br/>
69+
/// </para>
70+
/// <para>
71+
/// In the current design, this needs to be a reference type so values are synced.
72+
/// </para>
73+
/// </remarks>
74+
internal class PipelinePhase<TSubsystem> : PipelinePhase
75+
where TSubsystem : CliSubsystem
76+
{
77+
private TSubsystem? subsystem;
6778

79+
public PipelinePhase(SubsystemKind kind) : base(kind)
80+
{ }
6881

69-
// AddSubsystem(CliSubsystem subsystem, SubsystemPhase phase = SubsystemPhase.NotSpecified);
70-
71-
//public enum SubsystemPhase
72-
//{
73-
// NotSpecified = 0,
74-
// BeforeDiagram,
75-
// Diagram,
76-
// AfterDiagram,
77-
// BeforeCompletion,
78-
// Completion,
79-
// AfterCompletion,
80-
// BeforeHelp,
81-
// Help,
82-
// AfterHelp,
83-
// BeforeVersion,
84-
// Version,
85-
// AfterVersion,
86-
// BeforeErrorReporting,
87-
// ErrorReporting,
88-
// AfterErrorReporting,
89-
//}
90-
91-
// AddSubsystem(CliSubsystem subsystem, SubsystemPhase phase = SubsystemPhase.NotSpecified, PhaseTiming timing = PhaseTiming.Before);
92-
93-
//public enum SubsystemPhase
94-
//{
95-
// NotSpecified = 0,
96-
// Diagram,
97-
// Completion,
98-
// Help,
99-
// Version,
100-
// ErrorReporting,
101-
//}
102-
103-
//public enum PhaseTiming
104-
//{
105-
// Before = 0,
106-
// After
107-
//}
108-
109-
110-
///// <summary>
111-
///// Subsystem phases group subsystems that should be run at specific places in CLI processing and
112-
///// are used for high level ordering.
113-
///// </summary>
114-
///// <remarks>
115-
///// Order of operations:
116-
/////
117-
///// * Initialize is called for all subsystems, regardless of phase
118-
///// * ExecuteIfNeeded is called for subsystems in the EarlyReturn phase
119-
///// * ExecuteIfNeeded is called for subsystems in the Validate phase
120-
///// * ExecuteIfNeeded is called for subsystems in the Execute phase
121-
///// * ExecuteIfNeeded is called for subsystems in the Finish phase
122-
///// * Teardown is called for all subsystems, regardless of phase
123-
///// </remarks>
124-
//public enum SubsystemPhase
125-
//{
126-
// /// <summary>
127-
// /// Indicates a subsystem that never runs, and exists to support other subsystems. ValueSubsystem
128-
// /// is an example.
129-
// /// </summary>
130-
// /// <remarks>
131-
// /// Initialization runs first, teardown runs last - this is arbitrary and can be changed prior
132-
// /// to release if we have scenarios to justify.
133-
// /// </remarks>
134-
// None,
135-
136-
// /// <summary>
137-
// /// Indicates a subsystem is designed to shortcut execution and perform an action other than the
138-
// /// action indicated by the command. HelpSubsystem and VersionSubsystem are examples.
139-
// /// </summary>
140-
// /// <remarks>
141-
// /// EarlyReturn subsystems are differentiated from other execution because data validation has not
142-
// /// occurred. Because of this, data should not be used and should be assume to be questionable.
143-
// /// </remarks>
144-
// EarlyReturn,
145-
146-
// /// <summary>
147-
// /// Indicates a subsystem that validates data entered by the user.
148-
// /// </summary>
149-
// /// <remarks>
150-
// /// Errors are not reported, but are rather stored for later display. This may be reconsidered
151-
// /// if we keep track of which errors have been reported.
152-
// /// </remarks>
153-
// Validate,
154-
155-
// /// <summary>
156-
// /// Indicates a subsystem that executes using data entered by the user. The only known case is
157-
// /// the Invocation subsystem.
158-
// /// </summary>
159-
// Execute,
160-
161-
// /// <summary>
162-
// /// Indicates a subsystem that runs as the CLI part of processing is ending. ErrorReportingSubsystem
163-
// /// is an example, although we may rethink when errors are displayed.
164-
// /// </summary>
165-
// /// <remarks>
166-
// /// This is separate from the TearDown step, which is avaiable to all subsystems.
167-
// /// </remarks>
168-
// Finish,
169-
//}
170-
171-
82+
internal TSubsystem? Subsystem
83+
{
84+
get => subsystem;
85+
set
86+
{
87+
CliSubsystem = value;
88+
subsystem = value;
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)