reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit 5fc2a3e2b588cc8b0e99538c604386846ddaf224
parent 7f693fe7ce0a4dfc2a6a2e20066ff9826026e1cb
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Thu, 26 Nov 2015 20:03:44 -0500

parse version names

Diffstat:
Msrc/database.cpp | 20++++++++++++--------
Msrc/database.hpp | 18++++++++++--------
Msrc/database_v1.cpp | 42+++++++++++++++++++++++++++++++++++-------
Msrc/errors.hpp | 4++--
Msrc/main.cpp | 3---
Msrc/reapack.cpp | 11++++++++++-
Asrc/version.cpp | 37+++++++++++++++++++++++++++++++++++++
Asrc/version.hpp | 34++++++++++++++++++++++++++++++++++
Mtest/database.cpp | 35++++++++++++++++++++++++++---------
Mtest/database_v1.cpp | 44++++++++++++++++++++++++++++++++++++++------
Atest/db/v1/missing_version.xml | 7+++++++
Atest/db/v1/wrong_package_tag.xml | 5+++++
Atest/db/v1/wrong_version_tag.xml | 7+++++++
Atest/version.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
14 files changed, 272 insertions(+), 44 deletions(-)

diff --git a/src/database.cpp b/src/database.cpp @@ -9,25 +9,25 @@ DatabasePtr Database::load(const char *file) TiXmlDocument doc(file); if(!doc.LoadFile()) - throw database_error("failed to read database"); + throw reapack_error("failed to read database"); TiXmlHandle docHandle(&doc); TiXmlElement *root = doc.RootElement(); if(strcmp(root->Value(), "index")) - throw database_error("invalid database"); + throw reapack_error("invalid database"); int version = 0; root->Attribute("version", &version); if(!version) - throw database_error("invalid version"); + throw reapack_error("invalid version"); switch(version) { case 1: return loadV1(root); default: - throw database_error("unsupported version"); + throw reapack_error("unsupported version"); } } @@ -40,7 +40,7 @@ Category::Category(const std::string &name) : m_name(name) { if(m_name.empty()) - throw database_error("empty category name"); + throw reapack_error("empty category name"); } void Category::addPackage(PackagePtr pack) @@ -62,12 +62,16 @@ Package::Package(const Type type, : m_category(0), m_type(type), m_name(name), m_author(author) { if(m_type == Package::UnknownType) - throw database_error("unsupported package type"); + throw reapack_error("unsupported package type"); if(m_name.empty()) - throw database_error("empty package name"); + throw reapack_error("empty package name"); if(m_author.empty()) - throw database_error("empty package author"); + throw reapack_error("empty package author"); +} +void Package::addVersion(VersionPtr ver) +{ + m_versions.insert(ver); } diff --git a/src/database.hpp b/src/database.hpp @@ -5,6 +5,8 @@ #include <string> #include <vector> +#include "version.hpp" + class TiXmlElement; class Database; @@ -25,7 +27,6 @@ public: CategoryPtr category(const int i) const { return m_categories[i]; } private: - friend Category; static DatabasePtr loadV1(TiXmlElement *); std::vector<CategoryPtr> m_categories; @@ -42,10 +43,6 @@ public: PackagePtr package(const int i) const { return m_packages[i]; } private: - friend Package; - friend DatabasePtr Database::loadV1(TiXmlElement *); - static CategoryPtr loadV1(TiXmlElement *); - std::string m_name; std::vector<PackagePtr> m_packages; }; @@ -64,14 +61,19 @@ public: void setCategory(Category *cat) { m_category = cat; } Category *category() const { return m_category; } -private: - friend CategoryPtr Category::loadV1(TiXmlElement *); - static PackagePtr loadV1(TiXmlElement *); + Type type() const { return m_type; } + const std::string &name() const { return m_name; } + const std::string &author() const { return m_author; } + + void addVersion(VersionPtr ver); + const VersionSet &versions() const { return m_versions; } +private: Category *m_category; Type m_type; std::string m_name; std::string m_author; + VersionSet m_versions; }; #endif diff --git a/src/database_v1.cpp b/src/database_v1.cpp @@ -6,6 +6,10 @@ using namespace std; +static CategoryPtr LoadCategoryV1(TiXmlElement *); +static PackagePtr LoadPackageV1(TiXmlElement *); +static VersionPtr LoadVersionV1(TiXmlElement *); + DatabasePtr Database::loadV1(TiXmlElement *root) { DatabasePtr db = make_shared<Database>(); @@ -14,7 +18,7 @@ DatabasePtr Database::loadV1(TiXmlElement *root) TiXmlElement *catNode = root->FirstChildElement(); while(catNode) { - db->addCategory(Category::loadV1(catNode)); + db->addCategory(LoadCategoryV1(catNode)); catNode = catNode->NextSiblingElement(); } @@ -22,10 +26,10 @@ DatabasePtr Database::loadV1(TiXmlElement *root) return db; } -CategoryPtr Category::loadV1(TiXmlElement *catNode) +CategoryPtr LoadCategoryV1(TiXmlElement *catNode) { if(strcmp(catNode->Value(), "category")) - throw database_error("not a category"); + throw reapack_error("not a category"); const char *name = catNode->Attribute("name"); if(!name) name = ""; @@ -35,7 +39,7 @@ CategoryPtr Category::loadV1(TiXmlElement *catNode) TiXmlElement *packNode = catNode->FirstChildElement(); while(packNode) { - cat->addPackage(Package::loadV1(packNode)); + cat->addPackage(LoadPackageV1(packNode)); packNode = packNode->NextSiblingElement(); } @@ -43,10 +47,10 @@ CategoryPtr Category::loadV1(TiXmlElement *catNode) return cat; } -PackagePtr Package::loadV1(TiXmlElement *packNode) +PackagePtr LoadPackageV1(TiXmlElement *packNode) { if(strcmp(packNode->Value(), "reapack")) - throw database_error("not a package"); + throw reapack_error("not a package"); const char *name = packNode->Attribute("name"); if(!name) name = ""; @@ -57,5 +61,29 @@ PackagePtr Package::loadV1(TiXmlElement *packNode) const char *author = packNode->Attribute("author"); if(!author) author = ""; - return make_shared<Package>(Package::convertType(type), name, author); + PackagePtr pack = make_shared<Package>( + Package::convertType(type), name, author); + + TiXmlElement *verNode = packNode->FirstChildElement(); + + while(verNode) { + pack->addVersion(LoadVersionV1(verNode)); + + verNode = verNode->NextSiblingElement(); + } + + return pack; +} + +VersionPtr LoadVersionV1(TiXmlElement *verNode) +{ + if(strcmp(verNode->Value(), "version")) + throw reapack_error("not a version"); + + const char *name = verNode->Attribute("name"); + if(!name) name = ""; + + VersionPtr ver = make_shared<Version>(name); + + return ver; } diff --git a/src/errors.hpp b/src/errors.hpp @@ -3,9 +3,9 @@ #include <stdexcept> -class database_error : public std::runtime_error { +class reapack_error : public std::runtime_error { public: - database_error(const char *what) : std::runtime_error(what) {} + reapack_error(const char *what) : std::runtime_error(what) {} }; #endif diff --git a/src/main.cpp b/src/main.cpp @@ -1,5 +1,4 @@ #include "reapack.hpp" -#include "database.hpp" #include <cstdlib> @@ -24,8 +23,6 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT( printf("%s\n", GetResourcePath()); - DatabasePtr db = Database::load("/Users/cfillion/Programs/reapack/reapack.xml"); - reapack.init(instance, rec); reapack.setupAction("REAPACKMGR", "ReaPack: Package Manager", diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -1,5 +1,8 @@ #include "reapack.hpp" +#include "errors.hpp" +#include "database.hpp" + #include "reaper_plugin_functions.h" void ReaPack::init(REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec) @@ -31,5 +34,11 @@ bool ReaPack::execActions(const int id, const int) void ReaPack::toggleBrowser() { - ShowMessageBox("Hello World!", "Test", 0); + try { + Database::load("/Users/cfillion/Programs/reapack/reapack.xml"); + ShowMessageBox("Hello World!", "Test", 0); + } + catch(const reapack_error &e) { + ShowMessageBox(e.what(), "Database Error", 0); + } } diff --git a/src/version.cpp b/src/version.cpp @@ -0,0 +1,37 @@ +#include "version.hpp" + +#include "errors.hpp" + +#include <algorithm> +#include <cmath> +#include <regex> + +using namespace std; + +Version::Version(const std::string &str) + : m_name(str), m_code(0) +{ + static const regex pattern("(\\d+)"); + + auto begin = sregex_iterator(str.begin(), str.end(), pattern); + auto end = sregex_iterator(); + + if(begin == end) + throw reapack_error("invalid version name"); + + // set the major version by default + // even if there are less than 3 numeric components in the string + const int size = std::max(3L, distance(begin, end)); + + for(sregex_iterator it = begin; it != end; it++) { + const smatch match = *it; + const int index = distance(begin, it); + + m_code += stoi(match[1]) * pow(1000, size - index - 1); + } +} + +bool Version::operator<(const Version &o) +{ + return m_code < o.code(); +} diff --git a/src/version.hpp b/src/version.hpp @@ -0,0 +1,34 @@ +#ifndef REAPACK_VERSION_HPP +#define REAPACK_VERSION_HPP + +#include <set> +#include <string> + +class Version; +typedef std::shared_ptr<Version> VersionPtr; + +class Version { +public: + Version(const std::string &); + + const std::string &name() const { return m_name; } + int code() const { return m_code; } + + bool operator<(const Version &); + +private: + std::string m_name; + int m_code; +}; + +class VersionCompare { +public: + constexpr bool operator() (const VersionPtr &l, const VersionPtr &r) const + { + return *l.get() < *r.get(); + } +}; + +typedef std::set<VersionPtr, VersionCompare> VersionSet; + +#endif diff --git a/test/database.cpp b/test/database.cpp @@ -16,7 +16,7 @@ TEST_CASE("file not found", M) { Database::load(DBPATH "404.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "failed to read database"); } } @@ -26,7 +26,7 @@ TEST_CASE("broken xml", M) { Database::load(DBPATH "broken.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "failed to read database"); } } @@ -36,7 +36,7 @@ TEST_CASE("wrong root tag name", M) { Database::load(DBPATH "wrong_root.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "invalid database"); } } @@ -46,7 +46,7 @@ TEST_CASE("invalid version", M) { Database::load(DBPATH "invalid_version.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "invalid version"); } } @@ -56,7 +56,7 @@ TEST_CASE("future version", M) { Database::load(DBPATH "future_version.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "unsupported version"); } } @@ -98,7 +98,7 @@ TEST_CASE("empty category name", M) { Category cat{string()}; FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "empty category name"); } } @@ -108,7 +108,7 @@ TEST_CASE("unknown package type", M) { Package pack(Package::UnknownType, "a", "b"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "unsupported package type"); } } @@ -118,7 +118,7 @@ TEST_CASE("empty package name", M) { Package pack(Package::ScriptType, string(), "a"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "empty package name"); } } @@ -128,7 +128,24 @@ TEST_CASE("empty package author", M) { Package pack(Package::ScriptType, "a", string()); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "empty package author"); } } + +TEST_CASE("package versions are sorted", M) { + Package pack(Package::ScriptType, "a", "b"); + CHECK(pack.versions().size() == 0); + + pack.addVersion(make_shared<Version>("1")); + CHECK(pack.versions().size() == 1); + + pack.addVersion(make_shared<Version>("0.1")); + CHECK(pack.versions().size() == 2); + + const VersionSet &versions = pack.versions(); + auto it = versions.begin(); + + REQUIRE(it->get()->name() == "0.1"); + REQUIRE((++it)->get()->name() == "1"); +} diff --git a/test/database_v1.cpp b/test/database_v1.cpp @@ -9,14 +9,14 @@ using namespace std; -static const char *M = "[database_v1]"; +static const char *M = "[reapack_v1]"; TEST_CASE("unnamed category", M) { try { Database::load(DBPATH "unnamed_category.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "empty category name"); } } @@ -34,17 +34,27 @@ TEST_CASE("invalid category tag", M) { Database::load(DBPATH "wrong_category_tag.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "not a category"); } } +TEST_CASE("invalid package tag", M) { + try { + Database::load(DBPATH "wrong_package_tag.xml"); + FAIL(); + } + catch(const reapack_error &e) { + REQUIRE(string(e.what()) == "not a package"); + } +} + TEST_CASE("null package name", M) { try { Database::load(DBPATH "unnamed_package.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "empty package name"); } } @@ -54,7 +64,7 @@ TEST_CASE("null package type", M) { Database::load(DBPATH "missing_type.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "unsupported package type"); } } @@ -64,7 +74,29 @@ TEST_CASE("null author", M) { Database::load(DBPATH "anonymous_package.xml"); FAIL(); } - catch(const database_error &e) { + catch(const reapack_error &e) { REQUIRE(string(e.what()) == "empty package author"); } } + +TEST_CASE("invalid version tag", M) { + try { + Database::load(DBPATH "wrong_version_tag.xml"); + FAIL(); + } + catch(const reapack_error &e) { + REQUIRE(string(e.what()) == "not a version"); + } +} + + +TEST_CASE("null package version", M) { + try { + Database::load(DBPATH "missing_version.xml"); + FAIL(); + } + catch(const reapack_error &e) { + REQUIRE(string(e.what()) == "invalid version name"); + } +} + diff --git a/test/db/v1/missing_version.xml b/test/db/v1/missing_version.xml @@ -0,0 +1,7 @@ +<index version="1"> + <category name="a"> + <reapack name="a" author="b" type="script"> + <version /> + </reapack> + </category> +</index> diff --git a/test/db/v1/wrong_package_tag.xml b/test/db/v1/wrong_package_tag.xml @@ -0,0 +1,5 @@ +<index version="1"> + <category name="a"> + <hello /> + </category> +</index> diff --git a/test/db/v1/wrong_version_tag.xml b/test/db/v1/wrong_version_tag.xml @@ -0,0 +1,7 @@ +<index version="1"> + <category name="a"> + <reapack name="a" author="b" type="script"> + <hello /> + </reapack> + </category> +</index> diff --git a/test/version.cpp b/test/version.cpp @@ -0,0 +1,49 @@ +#include <catch.hpp> + +#include <errors.hpp> +#include <version.hpp> + +#include <string> + +using namespace std; + +static const char *M = "[database]"; +TEST_CASE("invalid") { + try { + Version ver("hello"); + FAIL(); + } + catch(const reapack_error &e) { + REQUIRE(string(e.what()) == "invalid version name"); + } +} + +TEST_CASE("major minor patch") { + Version ver("1.2.3"); + REQUIRE(ver.name() == "1.2.3"); + REQUIRE(ver.code() == 1002003); +} + +TEST_CASE("major minor") { + Version ver("1.2"); + REQUIRE(ver.name() == "1.2"); + REQUIRE(ver.code() == 1002000); +} + +TEST_CASE("major") { + Version ver("1"); + REQUIRE(ver.name() == "1"); + REQUIRE(ver.code() == 1000000); +} + +TEST_CASE("string suffix") { + Version ver("1.2pre3"); + REQUIRE(ver.name() == "1.2pre3"); + REQUIRE(ver.code() == 1002003); +} + +TEST_CASE("extra integer") { + Version ver("1.2.3.4"); + REQUIRE(ver.name() == "1.2.3.4"); + REQUIRE(ver.code() == 1002003004); +}