master
h 198 lines 7.45 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 ArgumentParser.h
8
9 Abstract:
10
11 Declaration of the ArgumentParser class for command-line argument parsing.
12
13 --*/
14 #pragma once
15 #include "Argument.h"
16 #include "Exceptions.h"
17 #include "Invocation.h"
18 #include "ArgMap.h"
19
20 #include <optional>
21 #include <string>
22 #include <string_view>
23 #include <vector>
24 #include <type_traits>
25
26 namespace wsl::windows::wslc {
27 // State machine is exposed so completion can run the parser, ignore errors,
28 // and inspect the in-progress state of the word being completed.
29 struct ParseArgumentsStateMachine
30 {
31 // optionsOnly: stop (without consuming) at the first positional token.
32 // stopOnUnknown: stop (without consuming) at the first unknown option
33 // token instead of throwing.
34 // overridableDefaults: ArgTypes whose existing entries in execArgs are
35 // treated as preloaded defaults (e.g. from environment
36 // variables). The first CLI Add for one of these types
37 // clears the preexisting entry first, so a preloaded
38 // default is replaced rather than appended to. Single-value
39 // args are last-wins regardless, so a later CLI duplicate
40 // simply overwrites; unlimited args accumulate once the
41 // preloaded default has been dropped.
42 ParseArgumentsStateMachine(
43 Invocation& inv,
44 ArgMap& execArgs,
45 std::vector<Argument> arguments,
46 bool optionsOnly = false,
47 bool stopOnUnknown = false,
48 const std::vector<Argument>& overridableDefaults = {});
49
50 ParseArgumentsStateMachine(const ParseArgumentsStateMachine&) = delete;
51 ParseArgumentsStateMachine& operator=(const ParseArgumentsStateMachine&) = delete;
52
53 ParseArgumentsStateMachine(ParseArgumentsStateMachine&&) = default;
54 ParseArgumentsStateMachine& operator=(ParseArgumentsStateMachine&&) = default;
55
56 // Returns false when there is nothing left to process.
57 bool Step();
58
59 void ThrowIfError() const;
60
61 // Empty state means the next argument can be anything.
62 struct State
63 {
64 State() = default;
65 State(ArgType type, std::wstring_view arg) : m_type(type), m_arg(arg)
66 {
67 }
68 State(ArgumentException ce) : m_exception(std::move(ce))
69 {
70 }
71
72 // If set, the next argument is a value for this type.
73 const std::optional<ArgType>& Type() const
74 {
75 return m_type;
76 }
77
78 const std::wstring& Arg() const
79 {
80 return m_arg;
81 }
82
83 const std::optional<ArgumentException>& Exception() const
84 {
85 return m_exception;
86 }
87
88 private:
89 std::optional<ArgType> m_type;
90 std::wstring m_arg;
91 std::optional<ArgumentException> m_exception;
92 };
93
94 const State& GetState() const
95 {
96 return m_state;
97 }
98
99 const Argument* NextPositional();
100
101 // Non-advancing variant of NextPositional.
102 bool HasNextPositional() const;
103
104 const std::vector<Argument>& Arguments() const
105 {
106 return m_arguments;
107 }
108
109 // In optionsOnly / stopOnUnknown modes this points at the first unconsumed token.
110 Invocation::iterator Position() const
111 {
112 return m_invocationItr;
113 }
114
115 private:
116 State StepInternal();
117 State ProcessPositionalArgument(const std::wstring_view& currArg);
118 State ProcessAnchoredPositionals(const std::wstring_view& currArg);
119 State ProcessAliasArgument(const std::wstring_view& currArg);
120 State ProcessNamedArgument(const std::wstring_view& currArg);
121 void ProcessAdjoinedValue(ArgType type, std::wstring_view value);
122
123 // Strips a single pair of surrounding double quotes from an adjoined value if present
124 // (e.g. --name="value" or --flag="true"). Shared by the value and flag adjoined-value
125 // paths so both treat quoted "=value" tokens identically.
126 static std::wstring_view StripSurroundingQuotes(std::wstring_view value);
127
128 void AdvanceToNextPositional(std::vector<Argument>::iterator& itr) const;
129
130 // Backs up one token and stops cleanly so Position() points at the unconsumed token.
131 State BackUpAndStop();
132
133 // Sets a boolean flag by storing its explicit parsed value (true or false). Clearing first
134 // collapses CLI duplicates to a single entry, so a repeated flag is docker-style last-wins
135 // (e.g. "--flag --flag=false" ends up false) and a duplicate "--flag --flag" folds to one
136 // entry. Consumers read the flag with ArgMap::GetValue(defaultValue),
137 // which lets a flag default to on and be disabled with "--flag=false".
138 void SetFlag(ArgType type, bool value);
139
140 // Parses an adjoined boolean token for a flag (e.g. the "false" in "--flag=false" or
141 // "-f=false"). A single pair of surrounding double quotes is stripped first (so
142 // "--flag=\"true\"" works like the value path), then the token is parsed as a Docker-style
143 // boolean (true/false/1/0/t/f, case-insensitive) and applied via SetFlag. Returns an error
144 // State if the token is not a recognized boolean. Shared by the alias, alias-chain, and
145 // named-flag paths so all three treat "=value" identically.
146 State ApplyFlagValue(ArgType type, std::wstring_view value, const std::wstring_view& currArg);
147
148 // Removes all entries for an argument and consumes any overridable-default slot,
149 // leaving the argument absent. This is the single-value (last-wins) primitive that
150 // SetFlag builds on; it is written to be reused for other single-value argument
151 // kinds in the future.
152 void ClearArgument(ArgType type);
153
154 // Stores a value for a Kind::Value argument. Single-value args are last-wins
155 // (any previous value, including a preloaded overridable default, is cleared
156 // first); unlimited args accumulate but still let the first CLI value replace a
157 // preloaded default.
158 void AddValue(ArgType type, std::wstring value);
159
160 // Returns the defined argument for a type, or nullptr if it is not one of this
161 // parser's arguments. Used to consult an argument's Limit while parsing values.
162 const Argument* FindArgument(ArgType type) const;
163
164 // If type is in m_overridableDefaults, removes any existing entry and
165 // consumes the override slot. Returns true if an override was consumed.
166 bool ConsumeOverrideIfPresent(ArgType type);
167
168 Invocation& m_invocation;
169 ArgMap& m_executionArgs;
170 std::vector<Argument> m_arguments;
171
172 Invocation::iterator m_invocationItr;
173 std::vector<Argument>::iterator m_positionalSearchItr;
174
175 // First positional processed; anchors handling of subsequent positionals/forwards.
176 std::optional<Argument> m_anchorPositional = std::nullopt;
177
178 std::vector<Argument> m_standardArgs = {};
179 std::vector<Argument> m_positionalArgs = {};
180 std::vector<Argument> m_forwardArgs = {};
181
182 State m_state;
183
184 // When true, stop cleanly at the first positional token (do not consume it).
185 bool m_optionsOnly = false;
186
187 // When true, stop cleanly (do not consume) at the first unknown option token.
188 bool m_stopOnUnknown = false;
189
190 // Set when m_optionsOnly or m_stopOnUnknown stopped processing.
191 bool m_stopped = false;
192
193 // ArgTypes whose preloaded value should be replaced by the first CLI add.
194 // Empties as overrides are consumed so a single preload can only be
195 // overridden once per parse.
196 std::vector<ArgType> m_overridableDefaults;
197 };
198 } // namespace wsl::windows::wslc