Compare commits
2 Commits
master
...
capturerep
Author | SHA1 | Date |
---|---|---|
Martchus | 96a0119a8d | |
Martchus | fc74d2b1de |
|
@ -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:
|
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
|
## Features
|
||||||
|
|
|
@ -27,6 +27,7 @@ set(SRC_FILES
|
||||||
syncthingdev.cpp
|
syncthingdev.cpp
|
||||||
syncthingconnection.cpp
|
syncthingconnection.cpp
|
||||||
syncthingconnection_requests.cpp
|
syncthingconnection_requests.cpp
|
||||||
|
syncthingconnection_capture_replay.cpp
|
||||||
syncthingconnectionsettings.cpp
|
syncthingconnectionsettings.cpp
|
||||||
syncthingnotifier.cpp
|
syncthingnotifier.cpp
|
||||||
syncthingconfig.cpp
|
syncthingconfig.cpp
|
||||||
|
|
|
@ -71,6 +71,8 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_syncthingUrl(syncthingUrl)
|
, m_syncthingUrl(syncthingUrl)
|
||||||
, m_apiKey(apiKey)
|
, m_apiKey(apiKey)
|
||||||
|
, m_captureFile(nullptr)
|
||||||
|
, m_replayFile(nullptr)
|
||||||
, m_status(SyncthingStatus::Disconnected)
|
, m_status(SyncthingStatus::Disconnected)
|
||||||
, m_statusComputionFlags(SyncthingStatusComputionFlags::Default)
|
, m_statusComputionFlags(SyncthingStatusComputionFlags::Default)
|
||||||
, m_keepPolling(false)
|
, m_keepPolling(false)
|
||||||
|
@ -120,7 +122,7 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
|
||||||
QObject::connect(&m_autoReconnectTimer, &QTimer::timeout, this, &SyncthingConnection::autoReconnect);
|
QObject::connect(&m_autoReconnectTimer, &QTimer::timeout, this, &SyncthingConnection::autoReconnect);
|
||||||
|
|
||||||
#ifdef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED
|
#ifdef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED
|
||||||
setupTestData();
|
setupTestData(); // TODO: remove this in favor of capturing/replay feature
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LIB_SYNCTHING_CONNECTOR_LOG_SYNCTHING_EVENTS
|
#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;
|
std::cerr << displayNames(devs).join(QStringLiteral(", ")).toStdString() << endl;
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
initCaptureReplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -304,6 +304,11 @@ private Q_SLOTS:
|
||||||
void handleAdditionalRequestCanceled();
|
void handleAdditionalRequestCanceled();
|
||||||
void recalculateStatus();
|
void recalculateStatus();
|
||||||
|
|
||||||
|
// capture/replay feature
|
||||||
|
void initCaptureReplay();
|
||||||
|
void captureResponse(std::string_view context, const QByteArray &response);
|
||||||
|
void replyCapturedResponses();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// internal helper methods
|
// internal helper methods
|
||||||
QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true);
|
QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true);
|
||||||
|
@ -321,6 +326,8 @@ private:
|
||||||
QByteArray m_apiKey;
|
QByteArray m_apiKey;
|
||||||
QString m_user;
|
QString m_user;
|
||||||
QString m_password;
|
QString m_password;
|
||||||
|
QFile *m_captureFile, *m_replayFile;
|
||||||
|
CppUtilities::DateTime m_lastReplyEvent;
|
||||||
SyncthingStatus m_status;
|
SyncthingStatus m_status;
|
||||||
SyncthingStatusComputionFlags m_statusComputionFlags;
|
SyncthingStatusComputionFlags m_statusComputionFlags;
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue