diff --git a/cli/commandtag.cpp b/cli/commandtag.cpp index f8da4fa..1186c70 100644 --- a/cli/commandtag.cpp +++ b/cli/commandtag.cpp @@ -3,14 +3,39 @@ #include "logger.h" #include "tagmanager.h" +bool CommandTag::ensureAbsolutePaths(const QVector &paths, QVector &absolutePaths) +{ + for(const QString &path : paths) + { + QFileInfo info{path}; + if(!info.exists()) + { + Logger::error() << "Can't add tag for file " + info.absoluteFilePath() + " because it does not exist" + << Qt::endl; + return false; + } + QString absolutePath = info.absoluteFilePath(); + if(!this->dbService->fileExistsInDatabase(absolutePath)) + { + Logger::error() << "Only files that have been indexed can be tagged. File not in index: " + absolutePath + << Qt::endl; + return false; + } + absolutePaths.append(absolutePath); + } + return true; +} + int CommandTag::handle(QStringList arguments) { QCommandLineParser parser; parser.addPositionalArgument("add", "Adds a tag to a file", "add [tag] [paths...]. Adds the tag to the specified paths"); - parser.addPositionalArgument("remove", "Removes a file associated to tag", "remove [tag] [file]"); + parser.addPositionalArgument("remove", "Removes a path associated to a tag", "remove [tag] [path]"); parser.addPositionalArgument("delete", "Deletes a tag", "delete [tag]"); parser.addPositionalArgument("list", "Lists paths associated with a tag, or all tags", "list [tag]"); + parser.addPositionalArgument("show", "Lists tags associated with a path", "show [path]"); + parser.addHelpOption(); parser.parse(arguments); @@ -21,9 +46,8 @@ int CommandTag::handle(QStringList arguments) parser.showHelp(EXIT_FAILURE); return EXIT_FAILURE; } - + TagManager tagManager{*this->dbService}; QString cmd = args[0]; - qDebug() << cmd; if(cmd == "add") { if(args.length() < 3) @@ -33,28 +57,14 @@ int CommandTag::handle(QStringList arguments) return EXIT_FAILURE; } QString tag = args[1]; - auto paths = args.mid(2).toVector(); - for(int i = 0; i < paths.size(); i++) - { - QFileInfo info{paths[i]}; - if(!info.exists()) - { - Logger::error() << "Can't add tag for file " + info.absoluteFilePath() + " because it does not exist" - << Qt::endl; - return EXIT_FAILURE; - } - QString absolutePath = info.absoluteFilePath(); - if(!this->dbService->fileExistsInDatabase(absolutePath)) - { - Logger::error() << "Only files that have been indexed can be tagged. File not in index: " + absolutePath - << Qt::endl; - return EXIT_FAILURE; - } - paths[i] = absolutePath; - } + QVector paths = args.mid(2).toVector(); - TagManager tagManager{*this->dbService}; - bool result = tagManager.addPathsToTag(tag, paths); + QVector absolutePaths; + if(!ensureAbsolutePaths(paths, absolutePaths)) + { + return EXIT_FAILURE; + } + bool result = tagManager.addPathsToTag(tag, absolutePaths); if(!result) { Logger::error() << "Failed to assign tags" << Qt::endl; @@ -62,6 +72,82 @@ int CommandTag::handle(QStringList arguments) } return EXIT_SUCCESS; } + if(cmd == "list") + { - return 0; + QString tag; + if(args.length() >= 2) + { + tag = args[1]; + } + QVector entries; + if(tag.isEmpty()) + { + entries = tagManager.getTags(); + } + else + { + entries = tagManager.getPaths(tag); + } + for(const QString &entry : entries) + { + Logger::info() << entry << Qt::endl; + } + } + if(cmd == "remove") + { + if(args.length() < 3) + { + Logger::error() << "Not enough arguments provided. 'remove' requires a tag followed by at least one path" + << Qt::endl; + return EXIT_FAILURE; + } + QString tag = args[1]; + QVector paths = args.mid(2).toVector(); + + QVector absolutePaths; + if(!ensureAbsolutePaths(paths, absolutePaths)) + { + return EXIT_FAILURE; + } + + if(!tagManager.removePathsForTag(tag, absolutePaths)) + { + Logger::error() << "Failed to remove path assignments" << Qt::endl; + return EXIT_FAILURE; + } + } + if(cmd == "delete") + { + if(args.length() != 2) + { + Logger::error() << "The 'delete' command requires the tag to delete" << Qt::endl; + return EXIT_FAILURE; + } + if(!tagManager.deleteTag(args[1])) + { + Logger::error() << "Failed to delete tag" << Qt::endl; + return EXIT_FAILURE; + } + } + if(cmd == "show") + { + if(args.length() != 2) + { + Logger::error() << "The 'show' command requires a path to show the assigned tags" << Qt::endl; + return EXIT_FAILURE; + } + QString path = args[1]; + QVector absolutePaths; + if(!ensureAbsolutePaths({path}, absolutePaths)) + { + return EXIT_FAILURE; + } + QVector tags = tagManager.getTags(absolutePaths.at(0)); + for(const QString &entry : tags) + { + Logger::info() << entry << Qt::endl; + } + } + return EXIT_SUCCESS; } diff --git a/cli/commandtag.h b/cli/commandtag.h index 5091443..bb8d31b 100644 --- a/cli/commandtag.h +++ b/cli/commandtag.h @@ -4,6 +4,9 @@ class CommandTag : public Command { + protected: + bool ensureAbsolutePaths(const QVector &paths, QVector &absolutePaths); + public: using Command::Command; diff --git a/shared/sqlitedbservice.cpp b/shared/sqlitedbservice.cpp index c508d6b..930346c 100644 --- a/shared/sqlitedbservice.cpp +++ b/shared/sqlitedbservice.cpp @@ -142,6 +142,27 @@ QVector SqliteDbService::getTagsForPath(QString path) return result; } +QVector SqliteDbService::getPathsForTag(QString tag) +{ + QVector result; + auto query = QSqlQuery(dbFactory->forCurrentThread()); + query.prepare( + "SELECT file.path FROM tag INNER JOIN filetag ON tag.id = filetag.tagid INNER JOIN file ON filetag.fileid " + "= file.id WHERE tag.name = ?"); + query.addBindValue(tag.toLower()); + query.setForwardOnly(true); + if(!query.exec()) + { + throw LooqsGeneralException("Error while trying to retrieve paths from database: " + query.lastError().text()); + } + while(query.next()) + { + QString path = query.value(0).toString(); + result.append(path); + } + return result; +} + bool SqliteDbService::setTags(QString path, const QSet &tags) { QSqlDatabase db = dbFactory->forCurrentThread(); @@ -379,3 +400,68 @@ bool SqliteDbService::addTag(QString tag, const QVector &paths) return true; } + +bool SqliteDbService::removePathsForTag(QString tag, const QVector &paths) +{ + QSqlDatabase db = dbFactory->forCurrentThread(); + QSqlQuery tagQuery(db); + QSqlQuery fileTagQuery(db); + + tag = tag.toLower(); + + fileTagQuery.prepare( + "DELETE FROM filetag WHERE fileid = (SELECT id FROM file WHERE path = ?) AND tagid = (SELECT id " + "FROM tag WHERE name = ?)"); + + fileTagQuery.bindValue(1, tag); + for(const QString &path : paths) + { + fileTagQuery.bindValue(0, path); + if(!fileTagQuery.exec()) + { + Logger::error() << "An error occured while trying to remove paths from tag assignment" << Qt::endl; + return false; + } + } + return true; +} + +bool SqliteDbService::deleteTag(QString tag) +{ + QSqlDatabase db = dbFactory->forCurrentThread(); + if(!db.transaction()) + { + Logger::error() << "Failed to open transaction while trying to delete tag " << tag << " : " << db.lastError() + << Qt::endl; + return false; + } + + tag = tag.toLower(); + QSqlQuery assignmentDeleteQuery(db); + assignmentDeleteQuery.prepare("DELETE FROM filetag WHERE tagid = (SELECT id FROM tag WHERE name = ?)"); + assignmentDeleteQuery.addBindValue(tag); + if(!assignmentDeleteQuery.exec()) + { + db.rollback(); + Logger::error() << "Error while trying to delete tag: " << db.lastError() << Qt::endl; + return false; + } + + QSqlQuery deleteTagQuery(db); + deleteTagQuery.prepare("DELETE FROM tag WHERE name = ?"); + deleteTagQuery.addBindValue(tag); + if(!deleteTagQuery.exec()) + { + db.rollback(); + Logger::error() << "Error while trying to delete tag: " << db.lastError() << Qt::endl; + return false; + } + + if(!db.commit()) + { + db.rollback(); + Logger::error() << "Error while trying to delete tag: " << db.lastError() << Qt::endl; + return false; + } + return true; +} diff --git a/shared/sqlitedbservice.h b/shared/sqlitedbservice.h index 6174d44..758818c 100644 --- a/shared/sqlitedbservice.h +++ b/shared/sqlitedbservice.h @@ -34,7 +34,10 @@ class SqliteDbService bool addTag(QString tag, const QVector &paths); QVector getTags(); QVector getTagsForPath(QString path); + QVector getPathsForTag(QString path); bool setTags(QString path, const QSet &tags); + bool removePathsForTag(QString tag, const QVector &paths); + bool deleteTag(QString tag); QVector search(const LooqsQuery &query); diff --git a/shared/tagmanager.cpp b/shared/tagmanager.cpp index 437ca85..945e5e4 100644 --- a/shared/tagmanager.cpp +++ b/shared/tagmanager.cpp @@ -28,6 +28,31 @@ bool TagManager::removeTagsForPath(QString path, const QSet &tags) return this->dbService->setTags(path, newTags); } +bool TagManager::removePathsForTag(QString tag, const QVector &paths) +{ + return this->dbService->removePathsForTag(tag, paths); +} + +bool TagManager::deleteTag(QString tag) +{ + return this->dbService->deleteTag(tag); +} + +QVector TagManager::getTags(QString path) +{ + return this->dbService->getTagsForPath(path); +} + +QVector TagManager::getTags() +{ + return this->dbService->getTags(); +} + +QVector TagManager::getPaths(QString tag) +{ + return this->dbService->getPathsForTag(tag); +} + bool TagManager::addTagsToPath(QString path, QString tagstring, QChar delim) { auto splitted = tagstring.split(delim); diff --git a/shared/tagmanager.h b/shared/tagmanager.h index 6c3d0ca..12ea430 100644 --- a/shared/tagmanager.h +++ b/shared/tagmanager.h @@ -17,9 +17,11 @@ class TagManager bool addPathsToTag(QString tag, const QVector &paths); bool removeTagsForPath(QString path, const QSet &tags); + bool removePathsForTag(QString tag, const QVector &paths); bool deleteTag(QString tag); QVector getTags(QString path); + QVector getTags(); QVector getPaths(QString tag); };