commit 5fc2a3e2b588cc8b0e99538c604386846ddaf224
parent 7f693fe7ce0a4dfc2a6a2e20066ff9826026e1cb
Author: cfillion <cfillion@users.noreply.github.com>
Date: Thu, 26 Nov 2015 20:03:44 -0500
parse version names
Diffstat:
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);
+}