Flow 2.0.0
Flow project: Full implementation reference.
option_set.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include <boost/algorithm/string.hpp>
21#include <boost/algorithm/string/trim_all.hpp>
22
23namespace flow::cfg
24{
25// Static/etc. initializers.
26
27/// @cond
28// -^- Doxygen, please ignore the following. It gets confused thinking this is a macro (@todo fix whenever).
29
30/* Just see the guy that uses it. I wish it could be a local `constexpr`. Apparently it cannot.
31 * @todo Use form R"(<literal regex, backslashes are backslashes, not C-string escapes>)":
32 * const boost::regex VALUE_SET_MEMBER_ID_TO_OPT_NAME_KEYED_REGEX{R"((.+)\[[^]]+\](.+))"};
33 * Leaving it alone for now; need to test first. */
34const boost::regex VALUE_SET_MEMBER_ID_TO_OPT_NAME_KEYED_REGEX{"(.+)\\[[^]]+\\](.+)"};
35
36// -v- Doxygen, please stop ignoring.
37/// @endcond
38
39// Implementations.
40
41std::string value_set_member_id_to_opt_name(util::String_view member_id, char sep_replacement)
42{
44 using std::string;
45 using boost::algorithm::replace_all;
46 using boost::algorithm::starts_with;
47 using boost::algorithm::is_any_of;
48 using boost::algorithm::trim_fill_if;
49 using std::replace;
50
51 /* E.g. m_cool_object.m_cool_sub_object->m_badass_sub_guy.m_cool_option_name
52 * => cool-object.cool-sub-object.badass-sub-guy.cool-option-name (with sep_replacement == '-');
53 * => cool_object.cool_sub_object.badass_sub_guy.cool_option_name (with sep_replacement == '_').
54 * Note, by the way, that in config files as parsed by boost.program_options, one can declare a section like:
55 * [cool-object]
56 * cool-option-1
57 * badass-option-2
58 * which will parse the 2 as cool-object.cool-option-1 and cool-object.badass-option-2. Synergy!
59 *
60 * Also #blah does not remove (at least some) white-space that's part of the original source code `blah`, so we have
61 * to eliminate all those first. For reference here's what gcc docs say about the "stringizing" algorithm. (I have
62 * not verified how this relates to C/C++ standards.)
63 * "All leading and trailing whitespace in text being stringized is ignored."
64 * - Nevertheless we'd still nuke it.
65 * "Any sequence of whitespace in the middle of the text is converted to a single space in the stringized result."
66 * - Cool, but in any case we nuke all space-y chars.
67 * "Comments are replaced by whitespace long before stringizing happens, so they never appear in stringized text."
68 * - That's good.
69 *
70 * Other activity involves auto-escaping string/char literals inside `blah`, but that doesn't apply to us. */
71
72 constexpr String_view M_PFX{"m_"};
73 constexpr String_view CONCAT_OK{"."}; // Dots remain (but if followed by m_, get rid of m_).
74 constexpr String_view CONCAT_REPLACED{"->"}; // -> become dots (and again if m_ follows, get rid of it).
75 constexpr char SEP_REPLACED = '_'; // From this...
76 // ...to sep_replacement (default '-' for config options; '_' for stats/metrics).
77
78 string opt_name{member_id};
79
80 /* Nuke space-y chars.
81 * Am paranoid about locales; and also didn't feel like using <locale> std::isspace(..., std::locale("C")). */
82 trim_fill_if(opt_name, "", is_any_of(" \f\n\r\t\v"));
83
84 // Should now be a contiguous identifier-composed compound.
85
86 // Eliminate leading M_PFX.
87 if (starts_with(opt_name, M_PFX))
88 {
89 opt_name.erase(0, M_PFX.size());
90 }
91
92 // Normalize object separators.
93 replace_all(opt_name, CONCAT_REPLACED, CONCAT_OK);
94
95 // Now any leading M_PFX gone; and object separators normalized to CONCAT_OK. Can eliminate remaining M_PFX:
96 replace_all(opt_name, string{CONCAT_OK} + string{M_PFX}, CONCAT_OK);
97
98 // Lastly transform the word-separators.
99 if (sep_replacement != SEP_REPLACED) // No-op if caller wants underscores retained.
100 {
101 replace(opt_name.begin(), opt_name.end(), SEP_REPLACED, sep_replacement);
102 }
103
104 return opt_name;
105} // value_set_member_id_to_opt_name()
106
107} // namespace flow::cfg
108
109namespace boost::filesystem
110{
111// Implementations.
112
113void validate(boost::any& target, const std::vector<std::string>& user_strings, path*, int)
114{
115 // Read our doc header for all background. Hence only light comments below.
116
119 namespace opts = flow::cfg::opts;
120 using boost::any;
121 using boost::lexical_cast;
122 using boost::bad_lexical_cast;
123 using std::string;
124
125 // Note this is auto-trimmed of spaces on left and right.
126 const string& user_string = opts::validators::get_single_string(user_strings);
127 /* That threw if there's more than 1 string. (I believe impossible in config file; but on command line they
128 * could try, e.g., "--log-path some-file.txt some-other-file.txt" -- that would throw.) */
129
130 path result_path; // Empty.
131 if (!user_string.empty())
132 {
133 /* If not empty then just do what the default validate() would've done: lexical_cast<> (i.e., istream>>) it.
134 * Not actually sure what could still trip the exception other than ""; but re-throw it with a nice error, because
135 * we can. */
136 try
137 {
138 result_path = lexical_cast<decltype(result_path)>(user_string);
139 }
140 catch (const bad_lexical_cast& exc)
141 {
142 throw Runtime_error{ostream_op_string
143 ("Error converting [", user_string,
144 "] to boost::filesystem path. Is there something strange in that string?")};
145 }
146 }
147 // else { Leave result_path at empty. lexical_cast<path>("") would have yielded bad_lexical_cast, but we allow it. }
148
149 target = result_path; // Load variable-type `target` with a `path`.
150} // validate(fs_path)
151
152} // namespace boost::filesystem
An std::runtime_error (which is an std::exception) that stores an Error_code.
Definition: error.hpp:49
We may add some ADL-based overloads into this namespace outside flow.
Definition: cfg_fwd.hpp:272
void validate(boost::any &target, const std::vector< std::string > &user_values, path *, int)
ADL-based overload of boost.program_options validate() to allow for empty boost::filesystem::path val...
Definition: option_set.cpp:113
Short-hand for namespace boost::program_options.
Flow module that facilitates configuring modules, such as applications and APIs, via statically and/o...
Definition: cfg_fwd.hpp:112
std::string value_set_member_id_to_opt_name(util::String_view member_id, char sep_replacement='-')
Utility, used by FLOW_CFG_OPTION_SET_DECLARE_OPTION() internally but made available as a public API i...
Definition: option_set.cpp:41
const boost::regex VALUE_SET_MEMBER_ID_TO_OPT_NAME_KEYED_REGEX
An internal constant for value_set_member_id_to_opt_name_keyed().
std::string ostream_op_string(T &&... ostream_args)
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the call...
Definition: util.hpp:575
Basic_string_view< char > String_view
Commonly used char-based Basic_string_view. See its doc header.