Skip to content

Commit 6b641d0

Browse files
Refactor arguments parsing - stage 2
1 parent cfc7d64 commit 6b641d0

19 files changed

Lines changed: 1444 additions & 387 deletions

.pre-commit-config.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ repos:
6060
^src/modules/complianceengine/src/lib/procedures/.*\.cpp$|
6161
^src/modules/complianceengine/src/lib/procedures/.*\.schema.json$
6262
)
63+
- repo: local
64+
hooks:
65+
- id: compliance-engine-interface
66+
name: Generate symbolic compliance engine interface
67+
types_or: [c++, json]
68+
entry: src/modules/complianceengine/src/lib/GenInterface.py
69+
language: python
70+
require_serial: true
71+
files: |
72+
(?x)(
73+
^src/modules/complianceengine/src/lib/payload.schema.json$|
74+
^src/modules/complianceengine/src/lib/procedures/.*\.h$|
75+
^src/modules/complianceengine/src/lib/procedures/.*\.schema.json$
76+
)
6377
- repo: https://github.com/pre-commit/mirrors-clang-format
6478
rev: v14.0.6
6579
hooks:
@@ -69,3 +83,8 @@ repos:
6983
^src/modules/complianceengine/.*\.h$|
7084
^src/modules/complianceengine/.*\.cpp$
7185
)
86+
exclude: |
87+
(?x)(
88+
^src/modules/complianceengine/src/lib/ProcedureMap2.h$|
89+
^src/modules/complianceengine/src/lib/ProcedureMap2.cpp$
90+
)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#include <Bindings.h>
5+
namespace ComplianceEngine
6+
{
7+
namespace BindingsImpl
8+
{
9+
// template<>
10+
// Optional<Error> ParseValue<std::string>(const std::map<std::string, std::string>& args, const std::string& key, std::string& output)
11+
// {
12+
// std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
13+
// << "key: " << key << std::endl;
14+
// auto it = args.find(key);
15+
// if (it == args.end())
16+
// {
17+
// return Error("Missing required '" + key + "' parameter", EINVAL);
18+
// }
19+
// output = it->second;
20+
// return Optional<Error>();
21+
// }
22+
//
23+
// Optional<Error> ParseValue(const std::map<std::string, std::string>& args, const std::string& key, int& output)
24+
// {
25+
// std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
26+
// << "key: " << key << std::endl;
27+
// auto it = args.find(key);
28+
// if (it == args.end())
29+
// {
30+
// return Error("Missing required '" + key + "' parameter", EINVAL);
31+
// }
32+
// auto result = TryStringToInt(it->second);
33+
// if (!result.HasValue())
34+
// {
35+
// return result.Error();
36+
// }
37+
//
38+
// output = std::move(result).Value();
39+
// return Optional<Error>();
40+
// }
41+
//
42+
// Optional<Error> ParseValue(const std::map<std::string, std::string>& args, const std::string& key, regex& output)
43+
// {
44+
// std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
45+
// << "key: " << key << std::endl;
46+
// auto it = args.find(key);
47+
// if (it == args.end())
48+
// {
49+
// return Error("Missing required '" + key + "' parameter", EINVAL);
50+
// }
51+
// try
52+
// {
53+
// output = regex(it->second);
54+
// }
55+
// catch (const std::exception& e)
56+
// {
57+
// return Error("Regular expression '" + it->second + "' compilation failed: " + e.what(), EINVAL);
58+
// }
59+
// return Optional<Error>();
60+
// }
61+
} // namespace BindingsImpl
62+
} // namespace ComplianceEngine
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#ifndef COMPLIANCEENGINE_BINDINGS_H
5+
#define COMPLIANCEENGINE_BINDINGS_H
6+
7+
#include "ProcedureMap2.h"
8+
9+
#include <CommonUtils.h>
10+
#include <Evaluator.h>
11+
#include <Optional.h>
12+
#include <Regex.h>
13+
#include <Result.h>
14+
#include <StringTools.h>
15+
#include <array>
16+
#include <iostream>
17+
#include <set>
18+
#include <type_traits>
19+
20+
namespace ComplianceEngine
21+
{
22+
23+
// Each procedure provides specialization for this structure
24+
template <typename Params>
25+
struct Bindings;
26+
27+
template <typename Enum>
28+
std::map<std::string, Enum> MapEnum();
29+
30+
namespace BindingsImpl
31+
{
32+
33+
template <typename...>
34+
struct make_void
35+
{
36+
typedef void type;
37+
};
38+
39+
// Missing in C++ 11
40+
template <typename... Ts>
41+
using void_t = typename make_void<Ts...>::type;
42+
43+
// Checks whether type T has a static method GetBindings
44+
template <typename T, typename = void>
45+
struct HasBindings : std::false_type
46+
{
47+
};
48+
49+
// Checks whether type T has a static method GetBindings
50+
template <typename T>
51+
struct HasBindings<T, void_t<decltype(T::GetBindings())>> : std::true_type
52+
{
53+
};
54+
55+
// Parsing overrides for supported types
56+
template <typename T>
57+
Optional<Error> ParseValue(const std::map<std::string, std::string>& args, const std::string& key, T& output);
58+
59+
template <>
60+
inline Optional<Error> ParseValue<std::string>(const std::map<std::string, std::string>& args, const std::string& key, std::string& output)
61+
{
62+
std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
63+
<< "key: " << key << std::endl;
64+
auto it = args.find(key);
65+
if (it == args.end())
66+
{
67+
return Error("Missing required '" + key + "' parameter", EINVAL);
68+
}
69+
output = std::move(it->second);
70+
return Optional<Error>();
71+
}
72+
73+
template <>
74+
inline Optional<Error> ParseValue<int>(const std::map<std::string, std::string>& args, const std::string& key, int& output)
75+
{
76+
std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
77+
<< "key: " << key << std::endl;
78+
auto it = args.find(key);
79+
if (it == args.end())
80+
{
81+
return Error("Missing required '" + key + "' parameter", EINVAL);
82+
}
83+
auto result = TryStringToInt(it->second);
84+
if (!result.HasValue())
85+
{
86+
return result.Error();
87+
}
88+
89+
output = std::move(result).Value();
90+
return Optional<Error>();
91+
}
92+
93+
template <>
94+
inline Optional<Error> ParseValue<regex>(const std::map<std::string, std::string>& args, const std::string& key, regex& output)
95+
{
96+
std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
97+
<< "key: " << key << std::endl;
98+
auto it = args.find(key);
99+
if (it == args.end())
100+
{
101+
return Error("Missing required '" + key + "' parameter", EINVAL);
102+
}
103+
try
104+
{
105+
output = regex(it->second);
106+
}
107+
catch (const std::exception& e)
108+
{
109+
return Error("Regular expression '" + it->second + "' compilation failed: " + e.what(), EINVAL);
110+
}
111+
return Optional<Error>();
112+
}
113+
114+
template <typename T>
115+
Optional<Error> ParseValue(const std::map<std::string, std::string>& args, const std::string& key, typename std::enable_if<std::is_enum<T>::value, T>::type& output)
116+
{
117+
std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
118+
<< "key: " << key << std::endl;
119+
auto it = args.find(key);
120+
if (it == args.end())
121+
{
122+
return Optional<Error>();
123+
}
124+
125+
static const auto enumMap = MapEnum<T>();
126+
auto enumIt = enumMap.find(it->second);
127+
if (enumIt == enumMap.end())
128+
{
129+
return Error("Invalid value '" + it->second + "' for enumeration parameter '" + key + "'", EINVAL);
130+
}
131+
132+
output = std::move(enumIt->second);
133+
return Optional<Error>();
134+
}
135+
136+
template <typename T>
137+
Optional<Error> ParseValue(const std::map<std::string, std::string>& args, const std::string& key, Optional<T>& output)
138+
{
139+
std::cerr << "[" << __func__ << ":" << __LINE__ << "] "
140+
<< "key: " << key << std::endl;
141+
auto it = args.find(key);
142+
if (it == args.end())
143+
{
144+
return Optional<Error>();
145+
}
146+
147+
T value;
148+
auto error = ParseValue<T>(args, key, value);
149+
if (error.HasValue())
150+
{
151+
return error;
152+
}
153+
154+
output = std::move(value);
155+
return Optional<Error>();
156+
}
157+
158+
template <typename Params>
159+
std::set<std::string> GetFieldNames()
160+
{
161+
using bindings = Bindings<Params>;
162+
static const auto names = bindings::names;
163+
return std::set<std::string>(names, names + bindings::size);
164+
}
165+
166+
template <std::size_t Index, typename Params>
167+
Optional<Error> ParseValueAt(const std::map<std::string, std::string>& args, typename std::enable_if<Index >= Bindings<Params>::size, Params>::type& output)
168+
{
169+
UNUSED(args);
170+
UNUSED(output);
171+
// End of recursion
172+
return Optional<Error>();
173+
}
174+
175+
template <std::size_t Index, typename Params>
176+
Optional<Error> ParseValueAt(const std::map<std::string, std::string>& args, typename std::enable_if < Index<Bindings<Params>::size, Params>::type & output)
177+
{
178+
using bindings = Bindings<Params>;
179+
const std::string name = bindings::names[Index];
180+
constexpr auto member = std::get<Index>(bindings::members);
181+
182+
auto error = ParseValue(args, name, output.*member);
183+
if (error.HasValue())
184+
{
185+
return error.Value();
186+
}
187+
188+
// Recurse to the next index
189+
return ParseValueAt<Index + 1, Params>(args, output);
190+
}
191+
192+
template <typename Params>
193+
Result<Params> ParseArguments(const std::map<std::string, std::string>& args)
194+
{
195+
using bindings = Bindings<Params>;
196+
197+
static_assert(std::is_default_constructible<Params>::value, "The parameters structure must be default constructible");
198+
static_assert(bindings::size, "The parameters structure must not be empty");
199+
200+
// Static as the bindings are immutable, and we don't need to recreate them all the time
201+
static const auto fields = GetFieldNames<Params>();
202+
203+
// Find arguments that are unsupported
204+
if (bindings::size < args.size())
205+
{
206+
return Error("Too many arguments provided", EINVAL);
207+
}
208+
209+
for (const auto& arg : args)
210+
{
211+
if (fields.end() == fields.find(arg.first))
212+
{
213+
return Error("Unknown parameter '" + arg.first + "'", EINVAL);
214+
}
215+
}
216+
217+
// T must be default constructible for this feature to work
218+
Params result;
219+
// Recursively consume arguments
220+
auto error = ParseValueAt<0, Params>(args, result);
221+
if (error.HasValue())
222+
{
223+
return error.Value();
224+
}
225+
226+
return result;
227+
}
228+
} // namespace BindingsImpl
229+
230+
template <typename Params>
231+
struct ProcedureHandler
232+
{
233+
Result<Status> (*procedure)(const Params&, IndicatorsTree&, ContextInterface&);
234+
ProcedureHandler(Result<Status> (*procedure)(const Params&, IndicatorsTree&, ContextInterface&))
235+
: procedure{procedure}
236+
{
237+
}
238+
239+
Result<Status> operator()(const std::map<std::string, std::string>& args, IndicatorsTree& indicators, ContextInterface& context) const
240+
{
241+
auto params = BindingsImpl::ParseArguments<Params>(args);
242+
if (!params.HasValue())
243+
{
244+
return params.Error();
245+
}
246+
return procedure(params.Value(), indicators, context);
247+
}
248+
};
249+
250+
template <typename T>
251+
ProcedureHandler<T> MakeHandler(Result<Status> (*fn)(const T&, IndicatorsTree&, ContextInterface&))
252+
{
253+
return ProcedureHandler<T>(fn);
254+
}
255+
} // namespace ComplianceEngine
256+
257+
#endif // COMPLIANCEENGINE_BINDINGS_H

src/modules/complianceengine/src/lib/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ add_library(complianceenginelib STATIC
135135
PasswordEntriesIterator.cpp
136136
Procedure.cpp
137137
ProcedureMap.cpp
138+
ProcedureMap2.cpp
138139
Result.cpp
139140
StringTools.cpp
140141
UsersIterator.cpp
@@ -160,4 +161,6 @@ target_link_libraries(complianceenginelib PRIVATE
160161

161162
target_include_directories(complianceenginelib PUBLIC
162163
${MODULES_INC_DIR}
163-
${CMAKE_CURRENT_SOURCE_DIR})
164+
${CMAKE_CURRENT_SOURCE_DIR}
165+
${CMAKE_CURRENT_SOURCE_DIR}/procedures
166+
)

0 commit comments

Comments
 (0)