1+ /* *
2+ * @file
3+ * @brief Implementation of OutputAction.
4+ * @author Damir Zainullin <[email protected] > 5+ *
6+ * @copyright Copyright (c) 2025 CESNET, z.s.p.o.
7+ */
8+
9+ #include " outputAction.hpp"
10+
11+ #include < algorithm>
12+ #include < cctype>
13+ #include < functional>
14+ #include < ranges>
15+ #include < stdexcept>
16+ #include < string>
17+ #include < variant>
18+ #include < vector>
19+
20+ namespace ipxp {
21+
22+ // Helper because std::isspace takes int
23+ constexpr static bool isSpace (const char c) noexcept
24+ {
25+ return std::isspace (c);
26+ }
27+
28+ OutputAction::OutputAction (std::string_view line)
29+ {
30+ if (std::ranges::any_of (line, isSpace)) {
31+ throw std::invalid_argument (
32+ " Invalid field specification: " + std::string (line)
33+ + " . No whitespace characters are allowed." );
34+ }
35+
36+ type = line.starts_with (' -' ) ? Type::Exclude : Type::Include;
37+ if (type == Type::Exclude) {
38+ line.remove_prefix (1 );
39+ }
40+
41+ if (line.contains (" ." )) {
42+ if (std::ranges::count (line, ' .' ) != 1 ) {
43+ throw std::invalid_argument (
44+ " Invalid field specification: " + std::string (line)
45+ + " . Only one '.' is allowed to separate plugin and field name." );
46+ }
47+ const std::size_t dotPos = line.find (' .' );
48+ action = FieldAction {
49+ .pluginName = std::string (line.substr (0 , dotPos)),
50+ .fieldName = std::string (line.substr (dotPos + 1 ))};
51+ return ;
52+ }
53+
54+ if (line == " *" ) {
55+ action = GlobalAction {};
56+ return ;
57+ }
58+
59+ action = PluginAction {std::string (line)};
60+ }
61+
62+ constexpr static std::string_view trimFieldsHeader (std::string_view content)
63+ {
64+ std::string_view header = " fields:" ;
65+ if (!content.starts_with (header)) {
66+ throw std::invalid_argument (" Configuration file must start with \" fields:\" header" );
67+ }
68+
69+ content.remove_prefix (header.size ());
70+ const std::size_t firstQuotePos
71+ = std::ranges::find_if (content, std::not_fn (isSpace)) - content.begin ();
72+ if (firstQuotePos == content.size () || content[firstQuotePos] != ' \' ' ) {
73+ throw std::invalid_argument (
74+ " Configuration file must contain opening quote after \" fields:\" header" );
75+ }
76+
77+ const std::size_t lastQuotePos = content.size () - 1
78+ - (std::find_if (content.rbegin (), content.rend (), std::not_fn (isSpace)) - content.rbegin ());
79+ if (lastQuotePos == -1ULL || content[lastQuotePos] != ' \' ' ) {
80+ throw std::invalid_argument (
81+ " Configuration file must contain closing quote after field definitions" );
82+ }
83+
84+ return content.substr (firstQuotePos + 1 , lastQuotePos - firstQuotePos - 1 );
85+ }
86+
87+ constexpr static std::string removeComments (std::string_view content) noexcept
88+ {
89+ std::string result (content.begin (), content.end ());
90+ for (std::size_t sharpPos = result.find (' #' ); sharpPos != std::string::npos;
91+ sharpPos = result.find (' #' , sharpPos)) {
92+ const std::size_t endOfLinePos = result.find (' \n ' , sharpPos);
93+ if (endOfLinePos == std::string::npos) {
94+ result.erase (sharpPos);
95+ } else {
96+ result.erase (sharpPos, endOfLinePos - sharpPos + 1 );
97+ }
98+ }
99+
100+ return result;
101+ }
102+
103+ std::vector<OutputAction> parseActions (std::string_view configurationContent)
104+ {
105+ return removeComments (trimFieldsHeader (configurationContent)) | std::views::split (' ,' )
106+ | std::views::transform ([](const auto lineRange) {
107+ return std::string_view (&*lineRange.begin (), std::ranges::distance (lineRange));
108+ })
109+ // Remove empty lines
110+ | std::views::filter ([](std::string_view line) {
111+ return std::ranges::find_if (line, std::not_fn (isSpace)) != line.end ();
112+ })
113+ // Trim spaces on the sides
114+ | std::views::transform ([](std::string_view line) {
115+ const std::size_t firstNonSpacePos
116+ = std::ranges::find_if (line, std::not_fn (isSpace)) - line.begin ();
117+ const std::size_t lastNonSpacePos = line.size () - 1
118+ - (std::find_if (line.rbegin (), line.rend (), std::not_fn (isSpace))
119+ - line.rbegin ());
120+ return line.substr (firstNonSpacePos, lastNonSpacePos - firstNonSpacePos + 1 );
121+ })
122+ | std::views::transform ([](std::string_view line) { return OutputAction (line); })
123+ | std::ranges::to<std::vector>();
124+ }
125+
126+ } // namespace ipxp
0 commit comments