Compare commits

...

2 Commits

5 changed files with 160 additions and 2 deletions

View File

@ -43,7 +43,7 @@ regarding positioning have no effect (see known bugs section). One can workaroun
the window manager how to place the window, e.g. under Sway one could add a configuration like this:
```
for_window [title="^Syncthing Tray( \(.*\))$"] floating enable, border none, resize set 450 400, move position 916 0
for_window [title="^Syncthing Tray( \(.*\))?$"] floating enable, border none, resize set 450 400, move position 916 0
```
## Features

View File

@ -27,6 +27,7 @@ set(SRC_FILES
syncthingdev.cpp
syncthingconnection.cpp
syncthingconnection_requests.cpp
syncthingconnection_capture_replay.cpp
syncthingconnectionsettings.cpp
syncthingnotifier.cpp
syncthingconfig.cpp

View File

@ -71,6 +71,8 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
: QObject(parent)
, m_syncthingUrl(syncthingUrl)
, m_apiKey(apiKey)
, m_captureFile(nullptr)
, m_replayFile(nullptr)
, m_status(SyncthingStatus::Disconnected)
, m_statusComputionFlags(SyncthingStatusComputionFlags::Default)
, m_keepPolling(false)
@ -120,7 +122,7 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
QObject::connect(&m_autoReconnectTimer, &QTimer::timeout, this, &SyncthingConnection::autoReconnect);
#ifdef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED
setupTestData();
setupTestData(); // TODO: remove this in favor of capturing/replay feature
#endif
#ifdef LIB_SYNCTHING_CONNECTOR_LOG_SYNCTHING_EVENTS
@ -133,6 +135,8 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
std::cerr << displayNames(devs).join(QStringLiteral(", ")).toStdString() << endl;
});
#endif
initCaptureReplay();
}
/*!

View File

@ -304,6 +304,11 @@ private Q_SLOTS:
void handleAdditionalRequestCanceled();
void recalculateStatus();
// capture/replay feature
void initCaptureReplay();
void captureResponse(std::string_view context, const QByteArray &response);
void replyCapturedResponses();
private:
// internal helper methods
QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true);
@ -321,6 +326,8 @@ private:
QByteArray m_apiKey;
QString m_user;
QString m_password;
QFile *m_captureFile, *m_replayFile;
CppUtilities::DateTime m_lastReplyEvent;
SyncthingStatus m_status;
SyncthingStatusComputionFlags m_statusComputionFlags;

View File

@ -0,0 +1,146 @@
#include "./syncthingconnection.h"
#include <c++utilities/conversion/conversionexception.h>
#include <c++utilities/conversion/stringconversion.h>
#include <QFile>
namespace Data {
/// \cond
static QString readEnvVar(const char *name)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
return qEnvironmentVariable(name);
#else
return QString::fromLocal8Bit(qgetenv(name));
#endif
}
/// \endcond
/*!
* \brief Initializes capturing/replay if corresponding environment variables are set.
*/
void SyncthingConnection::initCaptureReplay()
{
const auto captureFilePath = readEnvVar("SYNCTHING_TRAY_CAPTURE_FILE");
if (!captureFilePath.isEmpty()) {
m_captureFile = new QFile(captureFilePath, this);
m_captureFile->open(QFile::WriteOnly); // TODO: error handling
}
const auto replyFilePath = readEnvVar("SYNCTHING_TRAY_REPLAY_FILE");
if (!replyFilePath.isEmpty()) {
m_replayFile = new QFile(replyFilePath, this);
m_replayFile->open(QFile::ReadOnly); // TODO: error handling
}
}
/*!
* \brief Records the responsive if a capture file is initialized.
*/
void SyncthingConnection::captureResponse(std::string_view context, const QByteArray &response)
{
if (!m_captureFile) {
return;
}
const char marker[] = "--- ";
const auto space = ' ', newLine = '\n';
const auto timeStamp = CppUtilities::DateTime::exactGmtNow().toIsoString();
const auto size = CppUtilities::numberToString(response.size());
m_captureFile->write(marker, 4);
m_captureFile->write(timeStamp.data(), timeStamp.size());
m_captureFile->write(&space, 1);
m_captureFile->write(context.data(), context.size());
m_captureFile->write(&space, 1);
m_captureFile->write(response);
m_captureFile->write(&newLine, 1);
m_captureFile->write(response);
m_captureFile->write(&newLine, 1);
// TODO: error handling
}
/*!
* \brief Replays the captured responses if a reply file is initialized.
*/
void SyncthingConnection::replyCapturedResponses()
{
if (!m_replayFile) {
return;
}
enum State {
Marker0, Marker1, Marker2, Marker3, TimeStamp, Size, Context,
} state = Marker0;
auto c = char();
auto timeStampChars = std::string();
auto timeStamp = CppUtilities::DateTime();
auto sizeFactor = 1ul;
auto size = 0ul;
auto context = std::string();
while (m_replayFile->getChar(&c)) { // TODO: error handling
switch (state) {
case Marker0:
case Marker1:
case Marker2:
switch (c) {
case '-':
state = static_cast<State>(state + 1);
break;
default:
state = Marker0;
}
break;
case Marker3:
switch (c) {
case ' ':
state = TimeStamp;
timeStampChars.clear();
break;
default:
state = Marker0;
}
break;
case TimeStamp:
switch (c) {
case ' ':
try {
timeStamp = CppUtilities::DateTime::fromIsoStringGmt(timeStampChars.data());
state = Size;
sizeFactor = 0ul, size = 0ul;
} catch (const CppUtilities::ConversionException &c) {
state = Marker0; // TODO: error handling
}
break;
default:
timeStampChars += c;
}
break;
case Size:
if (c >= '0' && c <= '9') {
size += static_cast<decltype(size)>(c) * sizeFactor;
sizeFactor *= 10ul;
} else if (c == ' ') {
state = Context;
context.clear();
} else {
state = Marker0; // TODO: error handling
}
break;
case Context:
switch (c) {
case '\n':
state = Marker0;
m_replayFile->read(size);
// TODO: error handling
// TODO: add to some kind of queue for replays (by context)
// TODO: return if queue has enough items
break;
default:
context += c;
}
break;
}
}
}
} // namespace Data