cli: Allow specifying directory (items) by path
However, relative paths can only be matched to Syncthing dirs for the local instance.
This commit is contained in:
parent
40465a8abb
commit
2625f7b08b
|
@ -18,6 +18,7 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QStringBuilder>
|
||||
#include <QTimer>
|
||||
|
||||
#include <functional>
|
||||
|
@ -128,9 +129,9 @@ int Application::exec(int argc, const char *const *argv)
|
|||
}
|
||||
|
||||
// finally do the request or establish connection
|
||||
if (m_args.status.isPresent() || m_args.rescanAll.isPresent() || m_args.pauseAllDirs.isPresent() || m_args.pauseAllDevs.isPresent()
|
||||
|| m_args.resumeAllDirs.isPresent() || m_args.resumeAllDevs.isPresent() || m_args.pause.isPresent() || m_args.resume.isPresent()
|
||||
|| m_args.waitForIdle.isPresent() || m_args.pwd.isPresent()) {
|
||||
if (m_args.status.isPresent() || m_args.rescan.isPresent() || m_args.rescanAll.isPresent() || m_args.pauseAllDirs.isPresent()
|
||||
|| m_args.pauseAllDevs.isPresent() || m_args.resumeAllDirs.isPresent() || m_args.resumeAllDevs.isPresent() || m_args.pause.isPresent()
|
||||
|| m_args.resume.isPresent() || m_args.waitForIdle.isPresent() || m_args.pwd.isPresent()) {
|
||||
// those arguments rquire establishing a connection first, the actual handler is called by handleStatusChanged() when
|
||||
// the connection has been established
|
||||
m_connection.reconnect(m_settings);
|
||||
|
@ -324,25 +325,23 @@ void Application::requestRescan(const ArgumentOccurrence &occurrence)
|
|||
return;
|
||||
}
|
||||
|
||||
m_expectedResponse = occurrence.values.size();
|
||||
if (!m_expectedResponse) {
|
||||
cerr << Phrases::Error << "No directories specified." << Phrases::End << flush;
|
||||
exit(1);
|
||||
}
|
||||
m_expectedResponse = 0;
|
||||
connect(&m_connection, &SyncthingConnection::rescanTriggered, this, &Application::handleResponse);
|
||||
for (const char *value : occurrence.values) {
|
||||
cerr << "Request rescanning " << value << " ...\n";
|
||||
// split into directory name and relpath
|
||||
const char *firstSlash = value;
|
||||
for (; *firstSlash && *firstSlash != '/'; ++firstSlash)
|
||||
;
|
||||
if (*firstSlash) {
|
||||
m_connection.rescan(argToQString(value, static_cast<int>(firstSlash - value)), argToQString(firstSlash + 1));
|
||||
} else {
|
||||
m_connection.rescan(argToQString(value));
|
||||
const QString dirIdentifier(argToQString(value));
|
||||
const RelevantDir relevantDir(findDirectory(dirIdentifier));
|
||||
if (!relevantDir.dirObj) {
|
||||
continue;
|
||||
}
|
||||
relevantDir.notifyAboutRescan();
|
||||
m_connection.rescan(relevantDir.dirObj->id, relevantDir.subDir);
|
||||
++m_expectedResponse;
|
||||
}
|
||||
cerr.flush();
|
||||
if (!m_expectedResponse) {
|
||||
cerr << Phrases::Error << "No (valid) directories specified." << Phrases::End << flush;
|
||||
exit(1);
|
||||
}
|
||||
cerr << flush;
|
||||
}
|
||||
|
||||
void Application::requestRescanAll(const ArgumentOccurrence &)
|
||||
|
@ -371,8 +370,8 @@ void Application::requestPauseResume(bool pause)
|
|||
if (!m_relevantDirs.empty()) {
|
||||
QStringList dirIds;
|
||||
dirIds.reserve(m_relevantDirs.size());
|
||||
for (const SyncthingDir *dir : m_relevantDirs) {
|
||||
dirIds << dir->id;
|
||||
for (const RelevantDir &dir : m_relevantDirs) {
|
||||
dirIds << dir.dirObj->id;
|
||||
}
|
||||
if (pause) {
|
||||
cerr << "Request pausing directories ";
|
||||
|
@ -470,11 +469,10 @@ void Application::findRelevantDirsAndDevs(OperationType operationType)
|
|||
if (dirArg->isPresent()) {
|
||||
m_relevantDirs.reserve(dirArg->occurrences());
|
||||
for (size_t i = 0; i != dirArg->occurrences(); ++i) {
|
||||
if (const SyncthingDir *dir = m_connection.findDirInfo(argToQString(dirArg->values(i).front()), dummy)) {
|
||||
m_relevantDirs.emplace_back(dir);
|
||||
} else {
|
||||
cerr << Phrases::Warning << "Specified directory \"" << dirArg->values(i).front() << "\" does not exist and will be ignored"
|
||||
<< Phrases::End;
|
||||
const QString dirIdentifier(argToQString(dirArg->values(i).front()));
|
||||
const RelevantDir relevantDir(findDirectory(dirIdentifier));
|
||||
if (relevantDir.dirObj) {
|
||||
m_relevantDirs.emplace_back(move(relevantDir));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +486,7 @@ void Application::findRelevantDirsAndDevs(OperationType operationType)
|
|||
if (dev) {
|
||||
m_relevantDevs.emplace_back(dev);
|
||||
} else {
|
||||
cerr << Phrases::Warning << "Specified device \"" << devArg->values(i).front() << "\" does not exist and will be ignored"
|
||||
cerr << Phrases::Warning << "Specified device \"" << devArg->values(i).front() << "\" does not exist and will be ignored."
|
||||
<< Phrases::End;
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +496,7 @@ void Application::findRelevantDirsAndDevs(OperationType operationType)
|
|||
if (m_relevantDirs.empty() && m_relevantDevs.empty()) {
|
||||
m_relevantDirs.reserve(m_connection.dirInfo().size());
|
||||
for (const SyncthingDir &dir : m_connection.dirInfo()) {
|
||||
m_relevantDirs.emplace_back(&dir);
|
||||
m_relevantDirs.emplace_back(&dir, QString());
|
||||
}
|
||||
m_relevantDevs.reserve(m_connection.devInfo().size());
|
||||
for (const SyncthingDev &dev : m_connection.devInfo()) {
|
||||
|
@ -511,24 +509,24 @@ void Application::findRelevantDirsAndDevs(OperationType operationType)
|
|||
bool Application::findPwd()
|
||||
{
|
||||
const QString pwd(QDir::currentPath());
|
||||
for (const SyncthingDir &dir : m_connection.dirInfo()) {
|
||||
if (pwd == dir.pathWithoutTrailingSlash()) {
|
||||
m_pwd = &dir;
|
||||
return true;
|
||||
} else if (pwd.startsWith(dir.path)) {
|
||||
m_pwd = &dir;
|
||||
m_relativePath = pwd.mid(dir.path.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// find directory for working directory
|
||||
int dummy;
|
||||
m_pwd.dirObj = m_connection.findDirInfoByPath(pwd, m_pwd.subDir, dummy);
|
||||
if (m_pwd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle error
|
||||
cerr << Phrases::Error << "The current working directory \"" << pwd.toLocal8Bit().data() << "\" is not (part of) a Syncthing directory.";
|
||||
cerr << Phrases::End << flush;
|
||||
QCoreApplication::exit(2);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Application::printDir(const SyncthingDir *dir)
|
||||
void Application::printDir(const RelevantDir &relevantDir)
|
||||
{
|
||||
const SyncthingDir *const dir = relevantDir.dirObj;
|
||||
cout << " - ";
|
||||
setStyle(cout, TextAttribute::Bold);
|
||||
cout << dir->id.toLocal8Bit().data() << '\n';
|
||||
|
@ -674,8 +672,8 @@ void Application::waitForIdle(const ArgumentOccurrence &)
|
|||
|
||||
bool Application::checkWhetherIdle() const
|
||||
{
|
||||
for (const SyncthingDir *dir : m_relevantDirs) {
|
||||
switch (dir->status) {
|
||||
for (const RelevantDir &dir : m_relevantDirs) {
|
||||
switch (dir.dirObj->status) {
|
||||
case SyncthingDirStatus::Unknown:
|
||||
case SyncthingDirStatus::Idle:
|
||||
case SyncthingDirStatus::Unshared:
|
||||
|
@ -713,7 +711,7 @@ void Application::printPwdStatus(const ArgumentOccurrence &)
|
|||
if (!findPwd()) {
|
||||
return;
|
||||
}
|
||||
printDir(m_pwd);
|
||||
printDir(RelevantDir{ m_pwd });
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
|
@ -722,13 +720,9 @@ void Application::requestRescanPwd(const ArgumentOccurrence &)
|
|||
if (!findPwd()) {
|
||||
return;
|
||||
}
|
||||
if (m_relativePath.isEmpty()) {
|
||||
cerr << "Request rescanning directory \"" << m_pwd->path.toLocal8Bit().data() << "\" ..." << endl;
|
||||
} else {
|
||||
cerr << "Request rescanning item \"" << m_relativePath.toLocal8Bit().data() << "\" in directory \"" << m_pwd->path.toLocal8Bit().data()
|
||||
<< "\" ..." << endl;
|
||||
}
|
||||
m_connection.rescan(m_pwd->id, m_relativePath);
|
||||
|
||||
m_pwd.notifyAboutRescan();
|
||||
m_connection.rescan(m_pwd.dirObj->id, m_pwd.subDir);
|
||||
connect(&m_connection, &SyncthingConnection::rescanTriggered, this, &Application::handleResponse);
|
||||
m_expectedResponse = 1;
|
||||
}
|
||||
|
@ -738,13 +732,13 @@ void Application::requestPausePwd(const ArgumentOccurrence &)
|
|||
if (!findPwd()) {
|
||||
return;
|
||||
}
|
||||
if (m_connection.pauseDirectories(QStringList(m_pwd->id))) {
|
||||
cerr << "Request pausing directory \"" << m_pwd->path.toLocal8Bit().data() << "\" ..." << endl;
|
||||
if (m_connection.pauseDirectories(QStringList(m_pwd.dirObj->id))) {
|
||||
cerr << "Request pausing directory \"" << m_pwd.dirObj->path.toLocal8Bit().data() << "\" ..." << endl;
|
||||
connect(&m_connection, &SyncthingConnection::directoryPauseTriggered, this, &Application::handleResponse);
|
||||
m_preventDisconnect = true;
|
||||
m_expectedResponse = 1;
|
||||
} else {
|
||||
cerr << "Directory \"" << m_pwd->path.toLocal8Bit().data() << " already paused" << endl;
|
||||
cerr << "Directory \"" << m_pwd.dirObj->path.toLocal8Bit().data() << " already paused" << endl;
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
}
|
||||
|
@ -754,14 +748,14 @@ void Application::requestResumePwd(const ArgumentOccurrence &)
|
|||
if (!findPwd()) {
|
||||
return;
|
||||
}
|
||||
if (m_connection.resumeDirectories(QStringList(m_pwd->id))) {
|
||||
cerr << "Request resuming directory \"" << m_pwd->path.toLocal8Bit().data() << "\" ..." << endl;
|
||||
if (m_connection.resumeDirectories(QStringList(m_pwd.dirObj->id))) {
|
||||
cerr << "Request resuming directory \"" << m_pwd.dirObj->path.toLocal8Bit().data() << "\" ..." << endl;
|
||||
connect(&m_connection, &SyncthingConnection::directoryResumeTriggered, this, &Application::handleResponse);
|
||||
m_preventDisconnect = true;
|
||||
m_expectedResponse = 1;
|
||||
return;
|
||||
} else {
|
||||
cerr << "Directory \"" << m_pwd->path.toLocal8Bit().data() << " not paused" << endl;
|
||||
cerr << "Directory \"" << m_pwd.dirObj->path.toLocal8Bit().data() << " not paused" << endl;
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
}
|
||||
|
@ -793,4 +787,42 @@ void Application::initDevCompletion(Argument &arg, const ArgumentOccurrence &)
|
|||
arg.setPreDefinedCompletionValues(completionValues.join(QChar(' ')).toUtf8().data());
|
||||
}
|
||||
|
||||
RelevantDir Application::findDirectory(const QString &dirIdentifier)
|
||||
{
|
||||
int dummy;
|
||||
RelevantDir relevantDir;
|
||||
|
||||
// check whether the specified identifier is a known Syncthing directory or a relative path to an item in a
|
||||
// known Syncthing directory
|
||||
int firstSlash = dirIdentifier.indexOf(QChar('/'));
|
||||
relevantDir.dirObj = m_connection.findDirInfo(firstSlash >= 0 ? dirIdentifier.mid(0, firstSlash) : dirIdentifier, dummy);
|
||||
if (relevantDir) {
|
||||
if (firstSlash >= 0) {
|
||||
relevantDir.subDir = dirIdentifier.mid(firstSlash + 1);
|
||||
}
|
||||
return relevantDir;
|
||||
}
|
||||
|
||||
// check whether the specified identifier is an absolute or relative path of an item inside a known Syncthing directory
|
||||
relevantDir.dirObj = m_connection.findDirInfoByPath(
|
||||
QDir::isRelativePath(dirIdentifier) ? QDir::currentPath() % QChar('/') % dirIdentifier : dirIdentifier, relevantDir.subDir, dummy);
|
||||
if (relevantDir) {
|
||||
return relevantDir;
|
||||
}
|
||||
|
||||
cerr << Phrases::Warning << "Specified directory \"" << dirIdentifier.toLocal8Bit().data() << "\" is no Syncthing directory (or not part of any)."
|
||||
<< Phrases::End;
|
||||
return relevantDir;
|
||||
}
|
||||
|
||||
void RelevantDir::notifyAboutRescan() const
|
||||
{
|
||||
if (subDir.isEmpty()) {
|
||||
cerr << "Request rescanning directory \"" << dirObj->path.toLocal8Bit().data() << "\" ..." << endl;
|
||||
} else {
|
||||
cerr << "Request rescanning item \"" << subDir.toLocal8Bit().data() << "\" in directory \"" << dirObj->path.toLocal8Bit().data() << "\" ..."
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Cli
|
||||
|
|
|
@ -14,6 +14,26 @@ namespace Cli {
|
|||
|
||||
enum class OperationType { Status, PauseResume };
|
||||
|
||||
struct RelevantDir {
|
||||
RelevantDir(const Data::SyncthingDir *dir = nullptr, const QString &subDir = QString());
|
||||
operator bool() const;
|
||||
void notifyAboutRescan() const;
|
||||
|
||||
const Data::SyncthingDir *dirObj;
|
||||
QString subDir;
|
||||
};
|
||||
|
||||
inline RelevantDir::RelevantDir(const Data::SyncthingDir *dir, const QString &subDir)
|
||||
: dirObj(dir)
|
||||
, subDir(subDir)
|
||||
{
|
||||
}
|
||||
|
||||
inline RelevantDir::operator bool() const
|
||||
{
|
||||
return dirObj != nullptr;
|
||||
}
|
||||
|
||||
class Application : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -45,7 +65,7 @@ private:
|
|||
void requestPauseAllDirs(const ArgumentOccurrence &);
|
||||
void requestResumeAllDevs(const ArgumentOccurrence &);
|
||||
void requestResumeAllDirs(const ArgumentOccurrence &);
|
||||
static void printDir(const Data::SyncthingDir *dir);
|
||||
static void printDir(const RelevantDir &relevantDir);
|
||||
static void printDev(const Data::SyncthingDev *dev);
|
||||
void printStatus(const ArgumentOccurrence &);
|
||||
static void printLog(const std::vector<Data::SyncthingLogEntry> &logEntries);
|
||||
|
@ -58,6 +78,7 @@ private:
|
|||
void requestResumePwd(const ArgumentOccurrence &occurrence);
|
||||
void initDirCompletion(Argument &arg, const ArgumentOccurrence &);
|
||||
void initDevCompletion(Argument &arg, const ArgumentOccurrence &);
|
||||
RelevantDir findDirectory(const QString &dirIdentifier);
|
||||
|
||||
Args m_args;
|
||||
Data::SyncthingConnectionSettings m_settings;
|
||||
|
@ -65,10 +86,9 @@ private:
|
|||
size_t m_expectedResponse;
|
||||
bool m_preventDisconnect;
|
||||
bool m_callbacksInvoked;
|
||||
std::vector<const Data::SyncthingDir *> m_relevantDirs;
|
||||
std::vector<RelevantDir> m_relevantDirs;
|
||||
std::vector<const Data::SyncthingDev *> m_relevantDevs;
|
||||
const Data::SyncthingDir *m_pwd;
|
||||
QString m_relativePath;
|
||||
RelevantDir m_pwd;
|
||||
int m_idleDuration;
|
||||
int m_idleTimeout;
|
||||
bool m_argsRead;
|
||||
|
|
10
cli/args.cpp
10
cli/args.cpp
|
@ -37,7 +37,12 @@ Args::Args()
|
|||
, credentials("credentials", 'c', "specifies user name and password", { "user name", "password" })
|
||||
, certificate("cert", '\0', "specifies the certificate used by the Syncthing instance", { "path" })
|
||||
{
|
||||
for (Argument *arg : { &statusDir, &statusDev, &pauseDev, &pauseDir }) {
|
||||
for (Argument *arg : { &statusDir, &pauseDir }) {
|
||||
arg->setConstraints(0, Argument::varValueCount);
|
||||
arg->setValueCompletionBehavior(
|
||||
ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::Directories | ValueCompletionBehavior::InvokeCallback);
|
||||
}
|
||||
for (Argument *arg : { &statusDev, &pauseDev }) {
|
||||
arg->setConstraints(0, Argument::varValueCount);
|
||||
arg->setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::InvokeCallback);
|
||||
}
|
||||
|
@ -50,7 +55,8 @@ Args::Args()
|
|||
|
||||
rescan.setValueNames({ "dir ID" });
|
||||
rescan.setRequiredValueCount(Argument::varValueCount);
|
||||
rescan.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::InvokeCallback);
|
||||
rescan.setValueCompletionBehavior(
|
||||
ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::Directories | ValueCompletionBehavior::InvokeCallback);
|
||||
rescan.setExample(PROJECT_NAME " rescan dir1 dir2 dir4 dir5");
|
||||
pause.setSubArguments({ &pauseDir, &pauseDev });
|
||||
pause.setExample(PROJECT_NAME " pause --dir dir1 --dir dir2 --dev dev1 --dev dev2");
|
||||
|
|
Loading…
Reference in New Issue