Skip to content

Commit 003e825

Browse files
dherrera-metahenryiii
authored andcommitted
[precompile] Split Config.hpp
1 parent 3e5173d commit 003e825

File tree

4 files changed

+413
-361
lines changed

4 files changed

+413
-361
lines changed

CLI11.hpp.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ namespace {namespace} {{
6868

6969
{config_hpp}
7070

71+
{config_inl_hpp}
72+
7173
{formatter_hpp}
7274

7375
}} // namespace {namespace}

include/CLI/Config.hpp

Lines changed: 13 additions & 361 deletions
Original file line numberDiff line numberDiff line change
@@ -24,373 +24,25 @@ namespace CLI {
2424
// [CLI11:config_hpp:verbatim]
2525
namespace detail {
2626

27-
inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') {
28-
if(arg.empty()) {
29-
return std::string(2, stringQuote);
30-
}
31-
// some specifically supported strings
32-
if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
33-
return arg;
34-
}
35-
// floating point conversion can convert some hex codes, but don't try that here
36-
if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
37-
double val = 0.0;
38-
if(detail::lexical_cast(arg, val)) {
39-
return arg;
40-
}
41-
}
42-
// just quote a single non numeric character
43-
if(arg.size() == 1) {
44-
return std::string(1, characterQuote) + arg + characterQuote;
45-
}
46-
// handle hex, binary or octal arguments
47-
if(arg.front() == '0') {
48-
if(arg[1] == 'x') {
49-
if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
50-
return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
51-
})) {
52-
return arg;
53-
}
54-
} else if(arg[1] == 'o') {
55-
if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
56-
return arg;
57-
}
58-
} else if(arg[1] == 'b') {
59-
if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
60-
return arg;
61-
}
62-
}
63-
}
64-
if(arg.find_first_of(stringQuote) == std::string::npos) {
65-
return std::string(1, stringQuote) + arg + stringQuote;
66-
}
67-
return characterQuote + arg + characterQuote;
68-
}
27+
std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'');
6928

7029
/// Comma separated join, adds quotes if needed
71-
inline std::string ini_join(const std::vector<std::string> &args,
72-
char sepChar = ',',
73-
char arrayStart = '[',
74-
char arrayEnd = ']',
75-
char stringQuote = '"',
76-
char characterQuote = '\'') {
77-
std::string joined;
78-
if(args.size() > 1 && arrayStart != '\0') {
79-
joined.push_back(arrayStart);
80-
}
81-
std::size_t start = 0;
82-
for(const auto &arg : args) {
83-
if(start++ > 0) {
84-
joined.push_back(sepChar);
85-
if(!std::isspace<char>(sepChar, std::locale())) {
86-
joined.push_back(' ');
87-
}
88-
}
89-
joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
90-
}
91-
if(args.size() > 1 && arrayEnd != '\0') {
92-
joined.push_back(arrayEnd);
93-
}
94-
return joined;
95-
}
30+
std::string ini_join(const std::vector<std::string> &args,
31+
char sepChar = ',',
32+
char arrayStart = '[',
33+
char arrayEnd = ']',
34+
char stringQuote = '"',
35+
char characterQuote = '\'');
9636

97-
inline std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator) {
98-
std::vector<std::string> parents;
99-
if(detail::to_lower(section) != "default") {
100-
if(section.find(parentSeparator) != std::string::npos) {
101-
parents = detail::split(section, parentSeparator);
102-
} else {
103-
parents = {section};
104-
}
105-
}
106-
if(name.find(parentSeparator) != std::string::npos) {
107-
std::vector<std::string> plist = detail::split(name, parentSeparator);
108-
name = plist.back();
109-
detail::remove_quotes(name);
110-
plist.pop_back();
111-
parents.insert(parents.end(), plist.begin(), plist.end());
112-
}
113-
114-
// clean up quotes on the parents
115-
for(auto &parent : parents) {
116-
detail::remove_quotes(parent);
117-
}
118-
return parents;
119-
}
37+
std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator);
12038

12139
/// assuming non default segments do a check on the close and open of the segments in a configItem structure
122-
inline void
123-
checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
124-
125-
std::string estring;
126-
auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
127-
if(!output.empty() && output.back().name == "--") {
128-
std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
129-
while(output.back().parents.size() >= msize) {
130-
output.push_back(output.back());
131-
output.back().parents.pop_back();
132-
}
133-
134-
if(parents.size() > 1) {
135-
std::size_t common = 0;
136-
std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
137-
for(std::size_t ii = 0; ii < mpair; ++ii) {
138-
if(output.back().parents[ii] != parents[ii]) {
139-
break;
140-
}
141-
++common;
142-
}
143-
if(common == mpair) {
144-
output.pop_back();
145-
} else {
146-
while(output.back().parents.size() > common + 1) {
147-
output.push_back(output.back());
148-
output.back().parents.pop_back();
149-
}
150-
}
151-
for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
152-
output.emplace_back();
153-
output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
154-
output.back().name = "++";
155-
}
156-
}
157-
} else if(parents.size() > 1) {
158-
for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
159-
output.emplace_back();
160-
output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
161-
output.back().name = "++";
162-
}
163-
}
164-
165-
// insert a section end which is just an empty items_buffer
166-
output.emplace_back();
167-
output.back().parents = std::move(parents);
168-
output.back().name = "++";
169-
}
40+
void checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator);
17041
} // namespace detail
17142

172-
inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
173-
std::string line;
174-
std::string currentSection = "default";
175-
std::string previousSection = "default";
176-
std::vector<ConfigItem> output;
177-
bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
178-
bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
179-
bool inSection{false};
180-
char aStart = (isINIArray) ? '[' : arrayStart;
181-
char aEnd = (isINIArray) ? ']' : arrayEnd;
182-
char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
183-
int currentSectionIndex{0};
184-
while(getline(input, line)) {
185-
std::vector<std::string> items_buffer;
186-
std::string name;
187-
188-
detail::trim(line);
189-
std::size_t len = line.length();
190-
// lines have to be at least 3 characters to have any meaning to CLI just skip the rest
191-
if(len < 3) {
192-
continue;
193-
}
194-
if(line.front() == '[' && line.back() == ']') {
195-
if(currentSection != "default") {
196-
// insert a section end which is just an empty items_buffer
197-
output.emplace_back();
198-
output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
199-
output.back().name = "--";
200-
}
201-
currentSection = line.substr(1, len - 2);
202-
// deal with double brackets for TOML
203-
if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
204-
currentSection = currentSection.substr(1, currentSection.size() - 2);
205-
}
206-
if(detail::to_lower(currentSection) == "default") {
207-
currentSection = "default";
208-
} else {
209-
detail::checkParentSegments(output, currentSection, parentSeparatorChar);
210-
}
211-
inSection = false;
212-
if(currentSection == previousSection) {
213-
++currentSectionIndex;
214-
} else {
215-
currentSectionIndex = 0;
216-
previousSection = currentSection;
217-
}
218-
continue;
219-
}
220-
221-
// comment lines
222-
if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
223-
continue;
224-
}
225-
226-
// Find = in string, split and recombine
227-
auto pos = line.find(valueDelimiter);
228-
if(pos != std::string::npos) {
229-
name = detail::trim_copy(line.substr(0, pos));
230-
std::string item = detail::trim_copy(line.substr(pos + 1));
231-
auto cloc = item.find(commentChar);
232-
if(cloc != std::string::npos) {
233-
item.erase(cloc, std::string::npos); // NOLINT(readability-suspicious-call-argument)
234-
detail::trim(item);
235-
}
236-
if(item.size() > 1 && item.front() == aStart) {
237-
for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
238-
detail::trim(multiline);
239-
item += multiline;
240-
}
241-
items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
242-
} else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
243-
items_buffer = detail::split_up(item, aSep);
244-
} else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
245-
items_buffer = detail::split_up(item);
246-
} else {
247-
items_buffer = {item};
248-
}
249-
} else {
250-
name = detail::trim_copy(line);
251-
auto cloc = name.find(commentChar);
252-
if(cloc != std::string::npos) {
253-
name.erase(cloc, std::string::npos); // NOLINT(readability-suspicious-call-argument)
254-
detail::trim(name);
255-
}
256-
257-
items_buffer = {"true"};
258-
}
259-
if(name.find(parentSeparatorChar) == std::string::npos) {
260-
detail::remove_quotes(name);
261-
}
262-
// clean up quotes on the items
263-
for(auto &it : items_buffer) {
264-
detail::remove_quotes(it);
265-
}
266-
267-
std::vector<std::string> parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
268-
if(parents.size() > maximumLayers) {
269-
continue;
270-
}
271-
if(!configSection.empty() && !inSection) {
272-
if(parents.empty() || parents.front() != configSection) {
273-
continue;
274-
}
275-
if(configIndex >= 0 && currentSectionIndex != configIndex) {
276-
continue;
277-
}
278-
parents.erase(parents.begin());
279-
inSection = true;
280-
}
281-
if(!output.empty() && name == output.back().name && parents == output.back().parents) {
282-
output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
283-
} else {
284-
output.emplace_back();
285-
output.back().parents = std::move(parents);
286-
output.back().name = std::move(name);
287-
output.back().inputs = std::move(items_buffer);
288-
}
289-
}
290-
if(currentSection != "default") {
291-
// insert a section end which is just an empty items_buffer
292-
std::string ename;
293-
output.emplace_back();
294-
output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
295-
output.back().name = "--";
296-
while(output.back().parents.size() > 1) {
297-
output.push_back(output.back());
298-
output.back().parents.pop_back();
299-
}
300-
}
301-
return output;
302-
}
303-
304-
inline std::string
305-
ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
306-
std::stringstream out;
307-
std::string commentLead;
308-
commentLead.push_back(commentChar);
309-
commentLead.push_back(' ');
310-
311-
std::vector<std::string> groups = app->get_groups();
312-
bool defaultUsed = false;
313-
groups.insert(groups.begin(), std::string("Options"));
314-
if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
315-
out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
316-
}
317-
for(auto &group : groups) {
318-
if(group == "Options" || group.empty()) {
319-
if(defaultUsed) {
320-
continue;
321-
}
322-
defaultUsed = true;
323-
}
324-
if(write_description && group != "Options" && !group.empty()) {
325-
out << '\n' << commentLead << group << " Options\n";
326-
}
327-
for(const Option *opt : app->get_options({})) {
328-
329-
// Only process options that are configurable
330-
if(opt->get_configurable()) {
331-
if(opt->get_group() != group) {
332-
if(!(group == "Options" && opt->get_group().empty())) {
333-
continue;
334-
}
335-
}
336-
std::string name = prefix + opt->get_single_name();
337-
std::string value = detail::ini_join(
338-
opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
339-
340-
if(value.empty() && default_also) {
341-
if(!opt->get_default_str().empty()) {
342-
value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
343-
} else if(opt->get_expected_min() == 0) {
344-
value = "false";
345-
} else if(opt->get_run_callback_for_default()) {
346-
value = "\"\""; // empty string default value
347-
}
348-
}
349-
350-
if(!value.empty()) {
351-
if(write_description && opt->has_description()) {
352-
out << '\n';
353-
out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
354-
}
355-
out << name << valueDelimiter << value << '\n';
356-
}
357-
}
358-
}
359-
}
360-
auto subcommands = app->get_subcommands({});
361-
for(const App *subcom : subcommands) {
362-
if(subcom->get_name().empty()) {
363-
if(write_description && !subcom->get_group().empty()) {
364-
out << '\n' << commentLead << subcom->get_group() << " Options\n";
365-
}
366-
out << to_config(subcom, default_also, write_description, prefix);
367-
}
368-
}
369-
370-
for(const App *subcom : subcommands) {
371-
if(!subcom->get_name().empty()) {
372-
if(subcom->get_configurable() && app->got_subcommand(subcom)) {
373-
if(!prefix.empty() || app->get_parent() == nullptr) {
374-
out << '[' << prefix << subcom->get_name() << "]\n";
375-
} else {
376-
std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
377-
const auto *p = app->get_parent();
378-
while(p->get_parent() != nullptr) {
379-
subname = p->get_name() + parentSeparatorChar + subname;
380-
p = p->get_parent();
381-
}
382-
out << '[' << subname << "]\n";
383-
}
384-
out << to_config(subcom, default_also, write_description, "");
385-
} else {
386-
out << to_config(
387-
subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
388-
}
389-
}
390-
}
391-
392-
return out.str();
393-
}
394-
39543
// [CLI11:config_hpp:end]
39644
} // namespace CLI
45+
46+
#ifndef CLI11_COMPILE
47+
#include "impl/Config_inl.hpp"
48+
#endif

0 commit comments

Comments
 (0)