telemetry_json: Use the PImpl idiom to avoid unnecessary dependency exposure
Users of the web_service library shouldn't need to care about an external library like json.h. However, given it's exposed in our interface, this requires that other libraries publicly link in the JSON library. We can do better. By using the PImpl idiom, we can hide this dependency in the cpp file and remove the need to link that library in altogether.
This commit is contained in:
parent
131ce59800
commit
25038aeb0d
|
@ -2,93 +2,113 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <json.hpp>
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
#include "web_service/telemetry_json.h"
|
#include "web_service/telemetry_json.h"
|
||||||
#include "web_service/web_backend.h"
|
#include "web_service/web_backend.h"
|
||||||
|
|
||||||
namespace WebService {
|
namespace WebService {
|
||||||
|
|
||||||
TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
|
struct TelemetryJson::Impl {
|
||||||
: host(std::move(host)), username(std::move(username)), token(std::move(token)) {}
|
Impl(std::string host, std::string username, std::string token)
|
||||||
TelemetryJson::~TelemetryJson() = default;
|
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
|
||||||
|
|
||||||
|
nlohmann::json& TopSection() {
|
||||||
|
return sections[static_cast<u8>(Telemetry::FieldType::None)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const nlohmann::json& TopSection() const {
|
||||||
|
return sections[static_cast<u8>(Telemetry::FieldType::None)];
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) {
|
void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
|
||||||
sections[static_cast<u8>(type)][name] = value;
|
sections[static_cast<u8>(type)][name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) {
|
void SerializeSection(Telemetry::FieldType type, const std::string& name) {
|
||||||
TopSection()[name] = sections[static_cast<unsigned>(type)];
|
TopSection()[name] = sections[static_cast<unsigned>(type)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json output;
|
||||||
|
std::array<nlohmann::json, 7> sections;
|
||||||
|
std::string host;
|
||||||
|
std::string username;
|
||||||
|
std::string token;
|
||||||
|
};
|
||||||
|
|
||||||
|
TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
|
||||||
|
: impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
|
||||||
|
TelemetryJson::~TelemetryJson() = default;
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
|
impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
|
void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
|
||||||
Serialize(field.GetType(), field.GetName(), field.GetValue().count());
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelemetryJson::Complete() {
|
void TelemetryJson::Complete() {
|
||||||
SerializeSection(Telemetry::FieldType::App, "App");
|
impl->SerializeSection(Telemetry::FieldType::App, "App");
|
||||||
SerializeSection(Telemetry::FieldType::Session, "Session");
|
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
|
||||||
SerializeSection(Telemetry::FieldType::Performance, "Performance");
|
impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
|
||||||
SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
|
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
|
||||||
SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
|
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
|
||||||
SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
|
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
|
||||||
|
|
||||||
auto content = TopSection().dump();
|
auto content = impl->TopSection().dump();
|
||||||
// Send the telemetry async but don't handle the errors since they were written to the log
|
// Send the telemetry async but don't handle the errors since they were written to the log
|
||||||
Common::DetachedTasks::AddTask(
|
Common::DetachedTasks::AddTask(
|
||||||
[host{this->host}, username{this->username}, token{this->token}, content]() {
|
[host{impl->host}, username{impl->username}, token{impl->token}, content]() {
|
||||||
Client{host, username, token}.PostJson("/telemetry", content, true);
|
Client{host, username, token}.PostJson("/telemetry", content, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <json.hpp>
|
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/telemetry.h"
|
#include "common/telemetry.h"
|
||||||
|
|
||||||
|
@ -40,20 +38,8 @@ public:
|
||||||
void Complete() override;
|
void Complete() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nlohmann::json& TopSection() {
|
struct Impl;
|
||||||
return sections[static_cast<u8>(Telemetry::FieldType::None)];
|
std::unique_ptr<Impl> impl;
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void Serialize(Telemetry::FieldType type, const std::string& name, T value);
|
|
||||||
|
|
||||||
void SerializeSection(Telemetry::FieldType type, const std::string& name);
|
|
||||||
|
|
||||||
nlohmann::json output;
|
|
||||||
std::array<nlohmann::json, 7> sections;
|
|
||||||
std::string host;
|
|
||||||
std::string username;
|
|
||||||
std::string token;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace WebService
|
} // namespace WebService
|
||||||
|
|
Reference in New Issue