]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | #include <opentracing/mocktracer/json_recorder.h> |
2 | #include <opentracing/mocktracer/tracer.h> | |
3 | #include <opentracing/mocktracer/tracer_factory.h> | |
4 | #include <cctype> | |
5 | #include <cerrno> | |
6 | #include <cstring> | |
7 | #include <fstream> | |
8 | #include <stdexcept> | |
9 | #include <string> | |
10 | ||
11 | namespace opentracing { | |
12 | BEGIN_OPENTRACING_ABI_NAMESPACE | |
13 | namespace mocktracer { | |
14 | ||
15 | namespace { | |
16 | struct InvalidConfigurationError : public std::exception { | |
17 | public: | |
18 | InvalidConfigurationError(const char* position, std::string&& message) | |
19 | : position_{position}, message_{std::move(message)} {} | |
20 | ||
21 | const char* what() const noexcept override { return message_.c_str(); } | |
22 | ||
23 | const char* position() const { return position_; } | |
24 | ||
25 | private: | |
26 | const char* position_; | |
27 | std::string message_; | |
28 | }; | |
29 | } // namespace | |
30 | ||
31 | static void Consume(const char*& i, const char* last, string_view s) { | |
32 | if (static_cast<std::size_t>(std::distance(i, last)) < s.size()) { | |
33 | throw InvalidConfigurationError{i, | |
34 | std::string{"expected "} + std::string{s}}; | |
35 | } | |
36 | ||
37 | for (size_t index = 0; index < s.size(); ++index) { | |
38 | if (*i++ != s[index]) { | |
39 | throw InvalidConfigurationError{ | |
40 | i, std::string{"expected "} + | |
41 | std::string{s.data() + index, s.data() + s.size()}}; | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | static void ConsumeWhitespace(const char*& i, const char* last) { | |
47 | for (; i != last; ++i) { | |
48 | if (!std::isspace(*i)) { | |
49 | return; | |
50 | } | |
51 | } | |
52 | } | |
53 | ||
54 | static void ConsumeToken(const char*& i, const char* last, string_view token) { | |
55 | ConsumeWhitespace(i, last); | |
56 | Consume(i, last, token); | |
57 | } | |
58 | ||
59 | static std::string ParseFilename(const char*& i, const char* last) { | |
60 | ConsumeToken(i, last, "\""); | |
61 | std::string result; | |
62 | while (i != last) { | |
63 | if (*i == '\"') { | |
64 | ++i; | |
65 | return result; | |
66 | } | |
67 | if (*i == '\\') { | |
68 | throw InvalidConfigurationError{ | |
69 | i, "escaped characters are not supported in filename"}; | |
70 | } | |
71 | if (std::isprint(*i)) { | |
72 | result.push_back(*i); | |
73 | } else { | |
74 | throw InvalidConfigurationError{i, "invalid character"}; | |
75 | } | |
76 | ++i; | |
77 | } | |
78 | ||
79 | throw InvalidConfigurationError{i, R"(no matching ")"}; | |
80 | } | |
81 | ||
82 | static std::string ParseConfiguration(const char* i, const char* last) { | |
83 | ConsumeToken(i, last, "{"); | |
84 | ConsumeToken(i, last, R"("output_file")"); | |
85 | ConsumeToken(i, last, ":"); | |
86 | auto filename = ParseFilename(i, last); | |
87 | ConsumeToken(i, last, "}"); | |
88 | ConsumeWhitespace(i, last); | |
89 | if (i != last) { | |
90 | throw InvalidConfigurationError{i, "expected EOF"}; | |
91 | } | |
92 | ||
93 | return filename; | |
94 | } | |
95 | ||
96 | struct MockTracerConfiguration { | |
97 | std::string output_file; | |
98 | }; | |
99 | ||
100 | expected<std::shared_ptr<Tracer>> MockTracerFactory::MakeTracer( | |
101 | const char* configuration, std::string& error_message) const noexcept try { | |
102 | MockTracerConfiguration tracer_configuration; | |
103 | if (configuration == nullptr) { | |
104 | error_message = "configuration must not be null"; | |
105 | return make_unexpected(invalid_configuration_error); | |
106 | } | |
107 | try { | |
108 | tracer_configuration.output_file = ParseConfiguration( | |
109 | configuration, configuration + std::strlen(configuration)); | |
110 | } catch (const InvalidConfigurationError& e) { | |
111 | error_message = std::string{"Error parsing configuration at position "} + | |
112 | std::to_string(std::distance(configuration, e.position())) + | |
113 | ": " + e.what(); | |
114 | return make_unexpected(invalid_configuration_error); | |
115 | } | |
116 | ||
117 | errno = 0; | |
118 | std::unique_ptr<std::ostream> ostream{ | |
119 | new std::ofstream{tracer_configuration.output_file}}; | |
120 | if (!ostream->good()) { | |
121 | error_message = "failed to open file `"; | |
122 | error_message += tracer_configuration.output_file + "` ("; | |
123 | error_message += std::strerror(errno); | |
124 | error_message += ")"; | |
125 | return make_unexpected(invalid_configuration_error); | |
126 | } | |
127 | ||
128 | MockTracerOptions tracer_options; | |
129 | tracer_options.recorder = | |
130 | std::unique_ptr<Recorder>{new JsonRecorder{std::move(ostream)}}; | |
131 | ||
132 | return std::shared_ptr<Tracer>{new MockTracer{std::move(tracer_options)}}; | |
133 | } catch (const std::bad_alloc&) { | |
134 | return make_unexpected(std::make_error_code(std::errc::not_enough_memory)); | |
135 | } | |
136 | ||
137 | } // namespace mocktracer | |
138 | END_OPENTRACING_ABI_NAMESPACE | |
139 | } // namespace opentracing |