From bdf2f4803cf572dbe7a87091eef02b459c297c56 Mon Sep 17 00:00:00 2001 From: Sebastien MacDougall-Landry <60635017+EmperorPenguin18@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:57:34 -0500 Subject: [PATCH 1/4] Added sha256sum --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 2494492..40b4c04 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -9,7 +9,7 @@ source=("$pkgname-$pkgver.tar.gz::https://github.com/EmperorPenguin18/libscry/ar arch=('x86_64') license=('GPL3') depends=('curl' 'sqlite' 'rapidjson') -sha256sums=('') +sha256sums=('3c35bee0e7383f704f2ff59ae100a708c0c36772d0b1b375c481587a6cea3622') build () { cd "$srcdir/$pkgname-$pkgver" From 019b1b4e8e7420992548c2f1b9131978cfcef4d2 Mon Sep 17 00:00:00 2001 From: Sebastien MacDougall-Landry <60635017+EmperorPenguin18@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:58:58 -0500 Subject: [PATCH 2/4] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 256a7a9..5fd93ce 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ make install #as root ``` ## Documentation Available in [docs](docs/) +## Projects using libscry +[scrycli](https://github.com/EmperorPenguin18/scrycli) ## Legal From the API Docs: From 50368d9972828be8184188bf7d42847826bbe88c Mon Sep 17 00:00:00 2001 From: EmperorPenguin18 <60635017+EmperorPenguin18@users.noreply.github.com> Date: Mon, 21 Feb 2022 22:46:44 -0500 Subject: [PATCH 3/4] Improved code organization --- PKGBUILD | 2 +- libscry.cc | 443 ---------------------------------------------------- libscry.h | 108 ------------- makefile | 4 +- src/card.cc | 50 ++++++ src/card.h | 36 +++++ src/data.cc | 70 +++++++++ src/data.h | 27 ++++ src/list.cc | 47 ++++++ src/list.h | 28 ++++ src/scry.cc | 275 ++++++++++++++++++++++++++++++++ src/scry.h | 54 +++++++ src/web.cc | 53 +++++++ src/web.h | 20 +++ 14 files changed, 663 insertions(+), 554 deletions(-) delete mode 100644 libscry.cc delete mode 100644 libscry.h create mode 100644 src/card.cc create mode 100644 src/card.h create mode 100644 src/data.cc create mode 100644 src/data.h create mode 100644 src/list.cc create mode 100644 src/list.h create mode 100644 src/scry.cc create mode 100644 src/scry.h create mode 100644 src/web.cc create mode 100644 src/web.h diff --git a/PKGBUILD b/PKGBUILD index 40b4c04..63c4b0b 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -13,7 +13,7 @@ sha256sums=('3c35bee0e7383f704f2ff59ae100a708c0c36772d0b1b375c481587a6cea3622') build () { cd "$srcdir/$pkgname-$pkgver" - g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared libscry.cc -o libscry.so + g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared src/*.cc -o libscry.so } package () { diff --git a/libscry.cc b/libscry.cc deleted file mode 100644 index d82faa3..0000000 --- a/libscry.cc +++ /dev/null @@ -1,443 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#include "libscry.h" - -using namespace std; -using namespace rapidjson; -using namespace std::chrono; - -extern "C" Scry* create_object() { - return new Scry; -} - -extern "C" void destroy_object( Scry* object ) { - delete object; -} - -struct memory { - char *response; - size_t size; -}; - -static size_t cb(void *data, size_t size, size_t nmemb, void *userp) { - size_t realsize = size * nmemb; - struct memory *mem = (struct memory *)userp; - - char *ptr = (char *)realloc(mem->response, mem->size + realsize + 1); - if (ptr == NULL) return 0; // out of memory! - - mem->response = ptr; - memcpy(&(mem->response[mem->size]), data, realsize); - mem->size += realsize; - mem->response[mem->size] = 0; - - return realsize; -} - -Scry::Scry() { - curl_global_init(CURL_GLOBAL_ALL); - easyhandle = curl_easy_init(); - curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, cb); - curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1); - char * cachedir = getenv("XDG_CACHE_HOME"); - int rc; - if (cachedir != NULL) rc = sqlite3_open(strcat(cachedir, "/libscry.db"), &db); - else rc = sqlite3_open(strcat(getenv("HOME"), "/.cache/libscry.db"), &db); - if (rc) { - fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - exit(1); - } - db_init("Cards"); - db_init("Lists"); - db_init("Autocompletes"); -} - -Scry::~Scry() { - curl_easy_cleanup(easyhandle); - curl_global_cleanup(); - sqlite3_close(db); - while (!cards.empty()) { - delete cards.back(); - cards.pop_back(); - } - while (!lists.empty()) { - delete lists.back(); - lists.pop_back(); - } -} - -char * Scry::api_call(string url) { - curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str()); - struct memory chunk = {0}; - curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, (void *)&chunk); - CURLcode success = curl_easy_perform(easyhandle); - if (success != 0) { - fprintf(stderr, "Errored with CURLcode %i\n", success); - exit(success); - } - return chunk.response; -} - -string Scry::db_exec(string in) { - const char * cmd = in.c_str(); - sqlite3_stmt *stmt; - int rc = sqlite3_prepare_v2(db, cmd, -1, &stmt, NULL); - if (rc != SQLITE_OK) { - fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); - exit(1); - } - rc = sqlite3_step(stmt); - if (rc != SQLITE_ROW && rc != SQLITE_DONE) { - fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); - sqlite3_finalize(stmt); - exit(1); - } - string output; - string temp = in.substr(0, 6); - if (temp == "SELECT") output = string((char*)sqlite3_column_text(stmt, 0)); - sqlite3_finalize(stmt); - return output; -} - -void Scry::db_init(string table) { - db_exec("CREATE TABLE IF NOT EXISTS " + table + "(Key TEXT, Updated DATETIME, Value TEXT);"); -} - -bool Scry::db_check(string table, string search) { - string cmd = "SELECT COUNT(1) FROM " + table + " WHERE Key='" + search + "';"; - if (db_exec(cmd).compare("1") == 0) return true; - return false; -} - -string Scry::db_read(string table, string search, string column) { - string cmd = "SELECT " + column + " FROM " + table + " WHERE Key='" + search + "';"; - return db_exec(cmd); -} - -void Scry::db_write(string table, string key, string value) { - string cmd = "UPDATE " + table + " SET Updated=datetime(), Value='" + value + "' WHERE Key='" + key + "';"; - db_exec(cmd); -} - -void Scry::db_new(string table, string key, string value) { - string cmd = "INSERT INTO " + table + " VALUES ('" + key + "', datetime(), '" + value + "');"; - db_exec(cmd); -} - -const year_month_day Scry::parse(string datetime) { - string ys = datetime.substr(0,4); - string ms = datetime.substr(5,2); - string ds = datetime.substr(8,2); - year y(stoi(ys)); - month m (stoi(ms)); - day d (stoi(ds)); - const year_month_day output(y, m, d); - return output; -} - -int Scry::datecheck(string datetime) { - const time_point now{system_clock::now()}; - const year_month_day ymd{floor(now)}; - const year_month_day old = parse(datetime); - - int output; - if (static_cast(ymd.year()) > static_cast(old.year())) { - output = 1; - } else { - if (static_cast(ymd.month()) > static_cast(old.month())) { - output = 1; - } else { - if (static_cast(ymd.day()) > static_cast(old.day()) + 7) { - output = 1; - } else if (static_cast(ymd.day()) == static_cast(old.day()) + 7) { - output = 0; - } else { - output = -1; - } - } - } - - return output; -} - -vector Scry::explode(const string& str, const char& ch) { - string next; - vector result; - for (string::const_iterator it = str.begin(); it != str.end(); it++) { - if (*it == ch) { - if (!next.empty()) { - result.push_back(next); - next.clear(); - } - } else next += *it; - } - if (!next.empty()) result.push_back(next); - return result; -} - -string Scry::implode(const vector& strs, const char& ch) { - string result = ""; - for (auto it = strs.begin(); it != strs.end(); it++) { - result += (*it) + ch; - } - return result; -} - -string Scry::urlformat(string str) { - regex space(" "); - regex colon(":"); - str = regex_replace(str, space, "%20"); - str = regex_replace(str, colon, "%3A"); - return str; -} - -string Scry::nameformat(string str) { - regex apos("'"); - str = regex_replace(str, apos, "''"); - return str; -} - -List * Scry::cards_search(string query) { - query = urlformat(query); - string url = "https://api.scryfall.com/cards/search?q=" + query; - List * list = new List(this, api_call(url), false); - lists.push_back(list); - return list; -} - -string Scry::cachecard(List * list, bool recursive) { - string names = ""; - if (recursive) { - for (int i = 0; i < list->allcards().size(); i++) { - string name = nameformat(list->allcards()[i]->name()); - names += name + "\n"; - string temp = nameformat(list->allcards()[i]->json()); - if (i < list->cards().size()) { - if (db_check("Cards", name)) { - db_write("Cards", name, temp); - } else db_new("Cards", name, temp); - } - } - } else { - for (int i = 0; i < list->cards().size(); i++) { - string name = nameformat(list->cards()[i]->name()); - names += name + "\n"; - string temp = nameformat(list->cards()[i]->json()); - if (db_check("Cards", name)) { - db_write("Cards", name, temp); - } else db_new("Cards", name, temp); - } - } - names.pop_back(); - return names; -} - -List * Scry::cards_search_cache(string query) { - query = urlformat(query); - List * list; - - regex pages(".*&p"); smatch sm; regex_search(query, sm, pages); - string search = string(sm[0]).substr(0, sm[0].length()-2); - if (size(search) < 1) search = query; - if (db_check("Lists", search)) { - if (datecheck( db_read("Lists", search, "Updated") ) == 1) { - string url = "https://api.scryfall.com/cards/search?q=" + query; - list = new List(this, api_call(url), true); - lists.push_back(list); - db_write("Lists", search, cachecard(list, true)); - } else { - vector strvec = explode(db_read("Lists", search, "Value"), '\n'); - vector content; - for (int i = 0; i < strvec.size(); i++) - content.push_back( new Card( db_read("Cards", nameformat(strvec[i]), "Value").c_str() ) ); - list = new List( content ); - lists.push_back(list); - } - } else { - string url = "https://api.scryfall.com/cards/search?q=" + query; - list = new List(this, api_call(url), true); - lists.push_back(list); - string names = cachecard(list, false); - if (db_check("Lists", search)) { - string temp = nameformat( db_read("Lists", search, "Value") ); - db_write("Lists", search, names + "\n" + temp); - } else db_new("Lists", search, names); - } - - return list; -} - -Card * Scry::cards_named(string query) { - query = urlformat(query); - string url = "https://api.scryfall.com/cards/named?fuzzy=" + query; - Card * card = new Card(api_call(url)); - cards.push_back(card); - return card; -} - -Card * Scry::cards_named_cache(string query) { - query = urlformat(query); - Card * card; - query[0] = toupper(query[0]); - string name = nameformat(query); - - if (db_check("Cards", name)) { - if (datecheck( db_read("Cards", name, "Updated") ) == 1) { - card = cards_named(query); - db_write("Cards", name, nameformat(card->json())); - } else { - card = new Card( db_read("Cards", name, "Value").c_str() ); - cards.push_back(card); - } - } else { - card = cards_named(query); - db_new("Cards", name, nameformat(card->json())); - } - - return card; -} - -vector Scry::cards_autocomplete(string query) { - query = urlformat(query); - string url = "https://api.scryfall.com/cards/autocomplete?q=" + query; - Document doc; - doc.Parse(api_call(url)); - const Value& a = doc["data"]; - vector output; - for (auto& v : a.GetArray()) output.push_back(v.GetString()); - return output; -} - -vector Scry::cards_autocomplete_cache(string query) { - query = urlformat(query); - vector names; - - if (db_check("Autocompletes", query)) { - if (datecheck( db_read("Autocompletes", query, "Updated") ) == 1) { - names = cards_autocomplete(query); - string namestr = implode(names, '\n'); - db_write("Autocompletes", query, nameformat(namestr)); - } else { - names = explode(db_read("Autocompletes", query, "Value"), '\n'); - } - } else { - names = cards_autocomplete(query); - string namestr = implode(names, '\n'); - db_new("Autocompletes", query, nameformat(namestr)); - } - - return names; -} - -Card * Scry::cards_random() { - string url = "https://api.scryfall.com/cards/random"; - Card * card = new Card(api_call(url)); - cards.push_back(card); - return card; -} - -vector Scry::split(Card * card) { - Document doc; doc.Parse(card->json().c_str()); - vector output; - for (int i = 0; i < doc["card_faces"].Size(); i++) { - StringBuffer buffer; - Writer writer(buffer); - doc["card_faces"][i].Accept(writer); - Card * card = new Card(buffer.GetString()); - output.push_back(card); cards.push_back(card); - } - return output; -} - -Card::Card(const char * rawjson) { - data.Parse(rawjson); -} - -string Card::name() { - return data["name"].GetString(); -} - -string Card::mana_cost() { - if (!data.HasMember("mana_cost")) return ""; - return data["mana_cost"].GetString(); -} - -string Card::type_line() { - return data["type_line"].GetString(); -} - -string Card::oracle_text() { - if (!data.HasMember("oracle_text")) return ""; - return data["oracle_text"].GetString(); -} - -string Card::power() { - if (!data.HasMember("power")) return ""; - return data["power"].GetString(); -} - -string Card::toughness() { - if (!data.HasMember("toughness")) return ""; - return data["toughness"].GetString(); -} - -bool Card::dual_sided() { - return data.HasMember("card_faces"); -} - -string Card::json() { - StringBuffer buffer; - Writer writer(buffer); - data.Accept(writer); - return buffer.GetString(); -} - -List::List(Scry * scry, const char * rawjson, bool cache) { - Document doc; - doc.Parse(rawjson); - for (int i = 0; i < doc["data"].Size(); i++) { - StringBuffer buffer; - Writer writer(buffer); - doc["data"][i].Accept(writer); - content.push_back(new Card(buffer.GetString())); - } - if (doc["has_more"].GetBool()) { - string url = doc["next_page"].GetString(); - regex q("q=.*&"); - regex page("page=.*&q"); - smatch sm1; regex_search(url, sm1, q); - smatch sm2; regex_search(url, sm2, page); - string query = string(sm1[0]).substr(2, sm1[0].length()-2) + string(sm2[0]).substr(0, sm2[0].length()-2); - if (cache) nextpage = scry->cards_search_cache(query); - else nextpage = scry->cards_search(query); - } else nextpage = nullptr; -} - -List::List(vector input) { - content = input; - nextpage = nullptr; -} - -List::~List() { - while (!content.empty()) { - delete content.back(); - content.pop_back(); - } -} - -vector List::cards() { - return content; -} - -vector List::allcards() { - vector output = cards(); - if (nextpage != nullptr) { - vector append = nextpage->allcards(); - output.insert(output.end(), append.begin(), append.end()); - } - return output; -} diff --git a/libscry.h b/libscry.h deleted file mode 100644 index e3c1b91..0000000 --- a/libscry.h +++ /dev/null @@ -1,108 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace rapidjson; -using namespace std::chrono; - -class Card; -class List; -class Scry; - -///This class is used to represent a single card -class Card { - public: - Card(const char * rawjson); - - ///Get the name (Storm Crow) of the card. - virtual string name(); - ///Get the mana cost ({1}{U}) of the card. - virtual string mana_cost(); - ///Get the type line (Creature - Bird) of the card. - virtual string type_line(); - ///Get the oracle text (Flying) of the card. - virtual string oracle_text(); - ///Get the power (1) of the card. - virtual string power(); - ///Get the toughness (2) of the card. - virtual string toughness(); - ///Returns true if the card has two faces, false otherwise. - virtual bool dual_sided(); - ///Get the raw json text of the card provided by Scryfall. - virtual string json(); - private: - Document data; -}; - -///This class is used to represent a list of cards (returned from a search for example) -class List { - public: - List(Scry * scry, const char * rawjson, bool cache); - List(vector input); - ~List(); - - ///Returns a vector with all the cards on this page of the list. For cards on all pages see allcards(). - virtual vector cards(); - ///Cumulates cards from all pages of the list and returns them as a vector. - virtual vector allcards(); - private: - vector content; - List * nextpage; -}; - -///This class is the main interface to Scryfall. It handles all the API requests and caching. -class Scry { - public: - Scry(); - ~Scry(); - - ///Put in a query using Scryfall syntax (commander:g+type:legendary) as the argument. - virtual List * cards_search(string query); - ///Cached version of cards_search. - virtual List * cards_search_cache(string query); - ///Put in the name of a card as the argument. - virtual Card * cards_named(string query); - ///Cached version of cards_named. - virtual Card * cards_named_cache(string query); - ///Put in part of a card's name as the argument. - virtual vector cards_autocomplete(string query); - ///Cached version of cards_autocomplete. - virtual vector cards_autocomplete_cache(string query); - ///Returns a randomly selected card. - virtual Card * cards_random(); - ///Splits a card into it's multiple faces. Useful because some cards won't have certain data in the conglomerate. - virtual vector split(Card * card); - private: - CURL *easyhandle; - sqlite3 *db; - vector cards; - vector lists; - virtual char * api_call(string url); - virtual string db_exec(string in); - virtual void db_init(string table); - virtual bool db_check(string table, string search); - virtual string db_read(string table, string search, string column); - virtual void db_write(string table, string key, string value); - virtual void db_new(string table, string key, string value); - virtual int datecheck(string datetime); - virtual const year_month_day parse(string datetime); - virtual vector explode(const string& str, const char& ch); - virtual string implode(const vector& strs, const char& ch); - virtual string urlformat(string str); - virtual string nameformat(string str); - virtual string cachecard(List * list, bool recursive); -}; diff --git a/makefile b/makefile index ff2412f..733c16c 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ -build: libscry.h libscry.cc - g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared libscry.cc -o libscry.so +build: src/*.cc + g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared src/*.cc -o libscry.so install: libscry.h libscry.so @mkdir -p /usr/include/ diff --git a/src/card.cc b/src/card.cc new file mode 100644 index 0000000..08cc929 --- /dev/null +++ b/src/card.cc @@ -0,0 +1,50 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#include "card.h" + +using namespace std; + +Card::Card(const char * rawjson) { + data.Parse(rawjson); +} + +string Card::name() { + return data["name"].GetString(); +} + +string Card::mana_cost() { + if (!data.HasMember("mana_cost")) return ""; + return data["mana_cost"].GetString(); +} + +string Card::type_line() { + return data["type_line"].GetString(); +} + +string Card::oracle_text() { + if (!data.HasMember("oracle_text")) return ""; + return data["oracle_text"].GetString(); +} + +string Card::power() { + if (!data.HasMember("power")) return ""; + return data["power"].GetString(); +} + +string Card::toughness() { + if (!data.HasMember("toughness")) return ""; + return data["toughness"].GetString(); +} + +bool Card::dual_sided() { + return data.HasMember("card_faces"); +} + +string Card::json() { + StringBuffer buffer; + Writer writer(buffer); + data.Accept(writer); + return buffer.GetString(); +} diff --git a/src/card.h b/src/card.h new file mode 100644 index 0000000..ca7c23e --- /dev/null +++ b/src/card.h @@ -0,0 +1,36 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#pragma once +#include +#include +#include +#include + +using namespace std; +using namespace rapidjson; + +class Card { + public: + Card(const char * rawjson); + + ///Get the name (Storm Crow) of the card. + virtual string name(); + ///Get the mana cost ({1}{U}) of the card. + virtual string mana_cost(); + ///Get the type line (Creature - Bird) of the card. + virtual string type_line(); + ///Get the oracle text (Flying) of the card. + virtual string oracle_text(); + ///Get the power (1) of the card. + virtual string power(); + ///Get the toughness (2) of the card. + virtual string toughness(); + ///Returns true if the card has two faces, false otherwise. + virtual bool dual_sided(); + ///Get the raw json text of the card provided by Scryfall. + virtual string json(); + private: + Document data; +}; diff --git a/src/data.cc b/src/data.cc new file mode 100644 index 0000000..a12720f --- /dev/null +++ b/src/data.cc @@ -0,0 +1,70 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#include "data.h" + +using namespace std; + +DataAccess::DataAccess() { + char * cachedir = getenv("XDG_CACHE_HOME"); + int rc; + if (cachedir != NULL) rc = sqlite3_open(strcat(cachedir, "/libscry.db"), &db); + else rc = sqlite3_open(strcat(getenv("HOME"), "/.cache/libscry.db"), &db); + if (rc) { + fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + exit(1); + } +} + +DataAccess::~DataAccess() { + sqlite3_close(db); +} + +string DataAccess::db_exec(string in) { + const char * cmd = in.c_str(); + sqlite3_stmt *stmt; + int rc = sqlite3_prepare_v2(db, cmd, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); + exit(1); + } + rc = sqlite3_step(stmt); + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); + sqlite3_finalize(stmt); + exit(1); + } + string output; + string temp = in.substr(0, 6); + if (temp == "SELECT") output = string((char*)sqlite3_column_text(stmt, 0)); + sqlite3_finalize(stmt); + return output; +} + +void DataAccess::db_init(string table) { + db_exec("CREATE TABLE IF NOT EXISTS " + table + "(Key TEXT, Updated DATETIME, Value TEXT);"); +} + +bool DataAccess::db_check(string table, string search) { + string cmd = "SELECT COUNT(1) FROM " + table + " WHERE Key='" + search + "';"; + if (db_exec(cmd).compare("1") == 0) return true; + return false; +} + +string DataAccess::db_read(string table, string search, string column) { + string cmd = "SELECT " + column + " FROM " + table + " WHERE Key='" + search + "';"; + return db_exec(cmd); +} + +void DataAccess::db_write(string table, string key, string value) { + string cmd = "UPDATE " + table + " SET Updated=datetime(), Value='" + value + "' WHERE Key='" + key + "';"; + db_exec(cmd); +} + +void DataAccess::db_new(string table, string key, string value) { + string cmd = "INSERT INTO " + table + " VALUES ('" + key + "', datetime(), '" + value + "');"; + db_exec(cmd); +} + diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..3502464 --- /dev/null +++ b/src/data.h @@ -0,0 +1,27 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#pragma once +#include +#include +#include + +using namespace std; + +///This class is used to access the database +class DataAccess { + public: + DataAccess(); + ~DataAccess(); + + virtual void db_init(string table); + virtual bool db_check(string table, string search); + virtual string db_read(string table, string search, string column); + virtual void db_write(string table, string key, string value); + virtual void db_new(string table, string key, string value); + private: + sqlite3 *db; + virtual string db_exec(string in); +}; + diff --git a/src/list.cc b/src/list.cc new file mode 100644 index 0000000..dd8bfdb --- /dev/null +++ b/src/list.cc @@ -0,0 +1,47 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#include "list.h" + +using namespace std; +using namespace rapidjson; + +List::List(const char * rawjson) { + Document doc; + doc.Parse(rawjson); + for (int i = 0; i < doc["data"].Size(); i++) { + StringBuffer buffer; + Writer writer(buffer); + doc["data"][i].Accept(writer); + content.push_back(new Card(buffer.GetString())); + } + if (doc["has_more"].GetBool()) { + string url = doc["next_page"].GetString(); + regex q("q=.*&"); + regex page("page=.*&q"); + smatch sm1; regex_search(url, sm1, q); + smatch sm2; regex_search(url, sm2, page); + nextpage = string(sm1[0]).substr(2, sm1[0].length()-2) + string(sm2[0]).substr(0, sm2[0].length()-2); + } else nextpage = ""; +} + +List::List(vector input) { + content = input; + nextpage = nullptr; +} + +List::~List() { + while (!content.empty()) { + delete content.back(); + content.pop_back(); + } +} + +vector List::cards() { + return content; +} + +string List::nextPage() { + return nextpage; +} diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..b3b070f --- /dev/null +++ b/src/list.h @@ -0,0 +1,28 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#pragma once +#include +#include +#include +#include +#include +#include "card.h" + +///This class is used to represent a list of cards (returned from a search for example) +class List { + public: + List(const char * rawjson); + List(vector input); + ~List(); + + ///Returns a vector with all the cards on this page of the list. For cards on all pages see allcards(). + virtual vector cards(); + ///Returns the string of the url for the next page of a search result + virtual string nextPage(); + private: + vector content; + string nextpage; +}; + diff --git a/src/scry.cc b/src/scry.cc new file mode 100644 index 0000000..c93b107 --- /dev/null +++ b/src/scry.cc @@ -0,0 +1,275 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#include "scry.h" + +using namespace std; +using namespace rapidjson; +using namespace std::chrono; + +extern "C" Scry* create_object() { + return new Scry; +} + +extern "C" void destroy_object( Scry* object ) { + delete object; +} + +Scry::Scry() { + wa = new WebAccess(); + da = new DataAccess(); + da->db_init("Cards"); + da->db_init("Lists"); + da->db_init("Autocompletes"); +} + +Scry::~Scry() { + delete(wa); + delete(da); + while (!cards.empty()) { + delete cards.back(); + cards.pop_back(); + } + while (!lists.empty()) { + delete lists.back(); + lists.pop_back(); + } +} + +const year_month_day Scry::parse(string datetime) { + string ys = datetime.substr(0,4); + string ms = datetime.substr(5,2); + string ds = datetime.substr(8,2); + year y(stoi(ys)); + month m (stoi(ms)); + day d (stoi(ds)); + const year_month_day output(y, m, d); + return output; +} + +int Scry::datecheck(string datetime) { + const time_point now{system_clock::now()}; + const year_month_day ymd{floor(now)}; + const year_month_day old = parse(datetime); + + int output; + if (static_cast(ymd.year()) > static_cast(old.year())) { + output = 1; + } else { + if (static_cast(ymd.month()) > static_cast(old.month())) { + output = 1; + } else { + if (static_cast(ymd.day()) > static_cast(old.day()) + 7) { + output = 1; + } else if (static_cast(ymd.day()) == static_cast(old.day()) + 7) { + output = 0; + } else { + output = -1; + } + } + } + + return output; +} + +vector Scry::explode(const string& str, const char& ch) { + string next; + vector result; + for (string::const_iterator it = str.begin(); it != str.end(); it++) { + if (*it == ch) { + if (!next.empty()) { + result.push_back(next); + next.clear(); + } + } else next += *it; + } + if (!next.empty()) result.push_back(next); + return result; +} + +string Scry::implode(const vector& strs, const char& ch) { + string result = ""; + for (auto it = strs.begin(); it != strs.end(); it++) { + result += (*it) + ch; + } + return result; +} + +string Scry::urlformat(string str) { + regex space(" "); + regex colon(":"); + str = regex_replace(str, space, "%20"); + str = regex_replace(str, colon, "%3A"); + return str; +} + +string Scry::nameformat(string str) { + regex apos("'"); + str = regex_replace(str, apos, "''"); + return str; +} + +List * Scry::cards_search(string query) { + query = urlformat(query); + string url = "https://api.scryfall.com/cards/search?q=" + query; + List * list = new List(wa->api_call(url)); + lists.push_back(list); + return list; +} + +string Scry::cachecard(List * list, bool recursive) { + string names = ""; + vector cards = allcards(list, true); + if (recursive) { + for (int i = 0; i < cards.size(); i++) { + string name = nameformat(cards[i]->name()); + names += name + "\n"; + string temp = nameformat(cards[i]->json()); + if (i < list->cards().size()) { + if (da->db_check("Cards", name)) { + da->db_write("Cards", name, temp); + } else da->db_new("Cards", name, temp); + } + } + } else { + for (int i = 0; i < cards.size(); i++) { + string name = nameformat(cards[i]->name()); + names += name + "\n"; + string temp = nameformat(cards[i]->json()); + if (da->db_check("Cards", name)) { + da->db_write("Cards", name, temp); + } else da->db_new("Cards", name, temp); + } + } + names.pop_back(); + return names; +} + +List * Scry::cards_search_cache(string query) { + query = urlformat(query); + List * list; + + regex pages(".*&p"); smatch sm; regex_search(query, sm, pages); + string search = string(sm[0]).substr(0, sm[0].length()-2); + if (size(search) < 1) search = query; + if (da->db_check("Lists", search)) { + if (datecheck( da->db_read("Lists", search, "Updated") ) == 1) { + string url = "https://api.scryfall.com/cards/search?q=" + query; + list = new List(wa->api_call(url)); + lists.push_back(list); + da->db_write("Lists", search, cachecard(list, true)); + } else { + vector strvec = explode(da->db_read("Lists", search, "Value"), '\n'); + vector content; + for (int i = 0; i < strvec.size(); i++) + content.push_back( new Card( da->db_read("Cards", nameformat(strvec[i]), "Value").c_str() ) ); + list = new List( content ); + lists.push_back(list); + } + } else { + string url = "https://api.scryfall.com/cards/search?q=" + query; + list = new List(wa->api_call(url)); + lists.push_back(list); + string names = cachecard(list, false); + if (da->db_check("Lists", search)) { + string temp = nameformat( da->db_read("Lists", search, "Value") ); + da->db_write("Lists", search, names + "\n" + temp); + } else da->db_new("Lists", search, names); + } + + return list; +} + +Card * Scry::cards_named(string query) { + query = urlformat(query); + string url = "https://api.scryfall.com/cards/named?fuzzy=" + query; + Card * card = new Card(wa->api_call(url)); + cards.push_back(card); + return card; +} + +Card * Scry::cards_named_cache(string query) { + query = urlformat(query); + Card * card; + query[0] = toupper(query[0]); + string name = nameformat(query); + + if (da->db_check("Cards", name)) { + if (datecheck( da->db_read("Cards", name, "Updated") ) == 1) { + card = cards_named(query); + da->db_write("Cards", name, nameformat(card->json())); + } else { + card = new Card( da->db_read("Cards", name, "Value").c_str() ); + cards.push_back(card); + } + } else { + card = cards_named(query); + da->db_new("Cards", name, nameformat(card->json())); + } + + return card; +} + +vector Scry::cards_autocomplete(string query) { + query = urlformat(query); + string url = "https://api.scryfall.com/cards/autocomplete?q=" + query; + Document doc; + doc.Parse(wa->api_call(url)); + const Value& a = doc["data"]; + vector output; + for (auto& v : a.GetArray()) output.push_back(v.GetString()); + return output; +} + +vector Scry::cards_autocomplete_cache(string query) { + query = urlformat(query); + vector names; + + if (da->db_check("Autocompletes", query)) { + if (datecheck( da->db_read("Autocompletes", query, "Updated") ) == 1) { + names = cards_autocomplete(query); + string namestr = implode(names, '\n'); + da->db_write("Autocompletes", query, nameformat(namestr)); + } else { + names = explode(da->db_read("Autocompletes", query, "Value"), '\n'); + } + } else { + names = cards_autocomplete(query); + string namestr = implode(names, '\n'); + da->db_new("Autocompletes", query, nameformat(namestr)); + } + + return names; +} + +Card * Scry::cards_random() { + string url = "https://api.scryfall.com/cards/random"; + Card * card = new Card(wa->api_call(url)); + cards.push_back(card); + return card; +} + +vector Scry::split(Card * card) { + Document doc; doc.Parse(card->json().c_str()); + vector output; + for (int i = 0; i < doc["card_faces"].Size(); i++) { + StringBuffer buffer; + Writer writer(buffer); + doc["card_faces"][i].Accept(writer); + Card * card = new Card(buffer.GetString()); + output.push_back(card); cards.push_back(card); + } + return output; +} + +vector Scry::allcards(List * list, bool cache) { + vector output = list->cards(); + if (list->nextPage() != "") { + vector append; + if (cache) append = allcards(cards_search_cache(list->nextPage()), true); + else append = allcards(cards_search(list->nextPage()), false); + output.insert(output.end(), append.begin(), append.end()); + } + return output; +} diff --git a/src/scry.h b/src/scry.h new file mode 100644 index 0000000..f4c8599 --- /dev/null +++ b/src/scry.h @@ -0,0 +1,54 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#pragma once +#include +#include +#include "web.h" +#include "data.h" +#include "list.h" +#include "card.h" + +using namespace std; +using namespace rapidjson; +using namespace std::chrono; + +///This class is used to represent a single card +///This class is the main interface to Scryfall. It handles all the API requests and caching. +class Scry { + public: + Scry(); + ~Scry(); + + ///Put in a query using Scryfall syntax (commander:g+type:legendary) as the argument. + virtual List * cards_search(string query); + ///Cached version of cards_search. + virtual List * cards_search_cache(string query); + ///Put in the name of a card as the argument. + virtual Card * cards_named(string query); + ///Cached version of cards_named. + virtual Card * cards_named_cache(string query); + ///Put in part of a card's name as the argument. + virtual vector cards_autocomplete(string query); + ///Cached version of cards_autocomplete. + virtual vector cards_autocomplete_cache(string query); + ///Returns a randomly selected card. + virtual Card * cards_random(); + ///Splits a card into it's multiple faces. Useful because some cards won't have certain data in the conglomerate. + virtual vector split(Card * card); + ///Returns all cards from a search using the list representing the first page + virtual vector allcards(List * list, bool cache); + private: + WebAccess * wa; + DataAccess * da; + vector cards; + vector lists; + virtual int datecheck(string datetime); + virtual const year_month_day parse(string datetime); + virtual vector explode(const string& str, const char& ch); + virtual string implode(const vector& strs, const char& ch); + virtual string urlformat(string str); + virtual string nameformat(string str); + virtual string cachecard(List * list, bool recursive); +}; diff --git a/src/web.cc b/src/web.cc new file mode 100644 index 0000000..576627b --- /dev/null +++ b/src/web.cc @@ -0,0 +1,53 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#include "web.h" + +using namespace std; + +struct memory { + char *response; + size_t size; +}; + +static size_t cb(void *data, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct memory *mem = (struct memory *)userp; + + char *ptr = (char *)realloc(mem->response, mem->size + realsize + 1); + if (ptr == NULL) return 0; // out of memory! + + mem->response = ptr; + memcpy(&(mem->response[mem->size]), data, realsize); + mem->size += realsize; + mem->response[mem->size] = 0; + + return realsize; +} + +WebAccess::WebAccess() { + curl_global_init(CURL_GLOBAL_ALL); + easyhandle = curl_easy_init(); + curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, cb); + curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1); +} + +WebAccess::~WebAccess() { + curl_easy_cleanup(easyhandle); + curl_global_cleanup(); +} + +char * WebAccess::api_call(string url) { + curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str()); + struct memory chunk = {0}; + curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, (void *)&chunk); + CURLcode success = curl_easy_perform(easyhandle); + if (success != 0) { + fprintf(stderr, "Errored with CURLcode %i\n", success); + exit(success); + } + return chunk.response; +} + diff --git a/src/web.h b/src/web.h new file mode 100644 index 0000000..01d93e8 --- /dev/null +++ b/src/web.h @@ -0,0 +1,20 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#pragma once +#include +#include +#include + +using namespace std; + +class WebAccess { + public: + WebAccess(); + ~WebAccess(); + + virtual char * api_call(string url); + private: + CURL *easyhandle; +}; From 53669cb887b70926948eb95069c7d38ad8ff58a8 Mon Sep 17 00:00:00 2001 From: EmperorPenguin18 <60635017+EmperorPenguin18@users.noreply.github.com> Date: Mon, 21 Feb 2022 22:59:22 -0500 Subject: [PATCH 4/4] Revert "Improved code organization" This reverts commit 50368d9972828be8184188bf7d42847826bbe88c. --- PKGBUILD | 2 +- libscry.cc | 443 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libscry.h | 108 +++++++++++++ makefile | 4 +- src/card.cc | 50 ------ src/card.h | 36 ----- src/data.cc | 70 --------- src/data.h | 27 ---- src/list.cc | 47 ------ src/list.h | 28 ---- src/scry.cc | 275 -------------------------------- src/scry.h | 54 ------- src/web.cc | 53 ------- src/web.h | 20 --- 14 files changed, 554 insertions(+), 663 deletions(-) create mode 100644 libscry.cc create mode 100644 libscry.h delete mode 100644 src/card.cc delete mode 100644 src/card.h delete mode 100644 src/data.cc delete mode 100644 src/data.h delete mode 100644 src/list.cc delete mode 100644 src/list.h delete mode 100644 src/scry.cc delete mode 100644 src/scry.h delete mode 100644 src/web.cc delete mode 100644 src/web.h diff --git a/PKGBUILD b/PKGBUILD index 63c4b0b..40b4c04 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -13,7 +13,7 @@ sha256sums=('3c35bee0e7383f704f2ff59ae100a708c0c36772d0b1b375c481587a6cea3622') build () { cd "$srcdir/$pkgname-$pkgver" - g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared src/*.cc -o libscry.so + g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared libscry.cc -o libscry.so } package () { diff --git a/libscry.cc b/libscry.cc new file mode 100644 index 0000000..d82faa3 --- /dev/null +++ b/libscry.cc @@ -0,0 +1,443 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#include "libscry.h" + +using namespace std; +using namespace rapidjson; +using namespace std::chrono; + +extern "C" Scry* create_object() { + return new Scry; +} + +extern "C" void destroy_object( Scry* object ) { + delete object; +} + +struct memory { + char *response; + size_t size; +}; + +static size_t cb(void *data, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct memory *mem = (struct memory *)userp; + + char *ptr = (char *)realloc(mem->response, mem->size + realsize + 1); + if (ptr == NULL) return 0; // out of memory! + + mem->response = ptr; + memcpy(&(mem->response[mem->size]), data, realsize); + mem->size += realsize; + mem->response[mem->size] = 0; + + return realsize; +} + +Scry::Scry() { + curl_global_init(CURL_GLOBAL_ALL); + easyhandle = curl_easy_init(); + curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, cb); + curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1); + char * cachedir = getenv("XDG_CACHE_HOME"); + int rc; + if (cachedir != NULL) rc = sqlite3_open(strcat(cachedir, "/libscry.db"), &db); + else rc = sqlite3_open(strcat(getenv("HOME"), "/.cache/libscry.db"), &db); + if (rc) { + fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + exit(1); + } + db_init("Cards"); + db_init("Lists"); + db_init("Autocompletes"); +} + +Scry::~Scry() { + curl_easy_cleanup(easyhandle); + curl_global_cleanup(); + sqlite3_close(db); + while (!cards.empty()) { + delete cards.back(); + cards.pop_back(); + } + while (!lists.empty()) { + delete lists.back(); + lists.pop_back(); + } +} + +char * Scry::api_call(string url) { + curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str()); + struct memory chunk = {0}; + curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, (void *)&chunk); + CURLcode success = curl_easy_perform(easyhandle); + if (success != 0) { + fprintf(stderr, "Errored with CURLcode %i\n", success); + exit(success); + } + return chunk.response; +} + +string Scry::db_exec(string in) { + const char * cmd = in.c_str(); + sqlite3_stmt *stmt; + int rc = sqlite3_prepare_v2(db, cmd, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); + exit(1); + } + rc = sqlite3_step(stmt); + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); + sqlite3_finalize(stmt); + exit(1); + } + string output; + string temp = in.substr(0, 6); + if (temp == "SELECT") output = string((char*)sqlite3_column_text(stmt, 0)); + sqlite3_finalize(stmt); + return output; +} + +void Scry::db_init(string table) { + db_exec("CREATE TABLE IF NOT EXISTS " + table + "(Key TEXT, Updated DATETIME, Value TEXT);"); +} + +bool Scry::db_check(string table, string search) { + string cmd = "SELECT COUNT(1) FROM " + table + " WHERE Key='" + search + "';"; + if (db_exec(cmd).compare("1") == 0) return true; + return false; +} + +string Scry::db_read(string table, string search, string column) { + string cmd = "SELECT " + column + " FROM " + table + " WHERE Key='" + search + "';"; + return db_exec(cmd); +} + +void Scry::db_write(string table, string key, string value) { + string cmd = "UPDATE " + table + " SET Updated=datetime(), Value='" + value + "' WHERE Key='" + key + "';"; + db_exec(cmd); +} + +void Scry::db_new(string table, string key, string value) { + string cmd = "INSERT INTO " + table + " VALUES ('" + key + "', datetime(), '" + value + "');"; + db_exec(cmd); +} + +const year_month_day Scry::parse(string datetime) { + string ys = datetime.substr(0,4); + string ms = datetime.substr(5,2); + string ds = datetime.substr(8,2); + year y(stoi(ys)); + month m (stoi(ms)); + day d (stoi(ds)); + const year_month_day output(y, m, d); + return output; +} + +int Scry::datecheck(string datetime) { + const time_point now{system_clock::now()}; + const year_month_day ymd{floor(now)}; + const year_month_day old = parse(datetime); + + int output; + if (static_cast(ymd.year()) > static_cast(old.year())) { + output = 1; + } else { + if (static_cast(ymd.month()) > static_cast(old.month())) { + output = 1; + } else { + if (static_cast(ymd.day()) > static_cast(old.day()) + 7) { + output = 1; + } else if (static_cast(ymd.day()) == static_cast(old.day()) + 7) { + output = 0; + } else { + output = -1; + } + } + } + + return output; +} + +vector Scry::explode(const string& str, const char& ch) { + string next; + vector result; + for (string::const_iterator it = str.begin(); it != str.end(); it++) { + if (*it == ch) { + if (!next.empty()) { + result.push_back(next); + next.clear(); + } + } else next += *it; + } + if (!next.empty()) result.push_back(next); + return result; +} + +string Scry::implode(const vector& strs, const char& ch) { + string result = ""; + for (auto it = strs.begin(); it != strs.end(); it++) { + result += (*it) + ch; + } + return result; +} + +string Scry::urlformat(string str) { + regex space(" "); + regex colon(":"); + str = regex_replace(str, space, "%20"); + str = regex_replace(str, colon, "%3A"); + return str; +} + +string Scry::nameformat(string str) { + regex apos("'"); + str = regex_replace(str, apos, "''"); + return str; +} + +List * Scry::cards_search(string query) { + query = urlformat(query); + string url = "https://api.scryfall.com/cards/search?q=" + query; + List * list = new List(this, api_call(url), false); + lists.push_back(list); + return list; +} + +string Scry::cachecard(List * list, bool recursive) { + string names = ""; + if (recursive) { + for (int i = 0; i < list->allcards().size(); i++) { + string name = nameformat(list->allcards()[i]->name()); + names += name + "\n"; + string temp = nameformat(list->allcards()[i]->json()); + if (i < list->cards().size()) { + if (db_check("Cards", name)) { + db_write("Cards", name, temp); + } else db_new("Cards", name, temp); + } + } + } else { + for (int i = 0; i < list->cards().size(); i++) { + string name = nameformat(list->cards()[i]->name()); + names += name + "\n"; + string temp = nameformat(list->cards()[i]->json()); + if (db_check("Cards", name)) { + db_write("Cards", name, temp); + } else db_new("Cards", name, temp); + } + } + names.pop_back(); + return names; +} + +List * Scry::cards_search_cache(string query) { + query = urlformat(query); + List * list; + + regex pages(".*&p"); smatch sm; regex_search(query, sm, pages); + string search = string(sm[0]).substr(0, sm[0].length()-2); + if (size(search) < 1) search = query; + if (db_check("Lists", search)) { + if (datecheck( db_read("Lists", search, "Updated") ) == 1) { + string url = "https://api.scryfall.com/cards/search?q=" + query; + list = new List(this, api_call(url), true); + lists.push_back(list); + db_write("Lists", search, cachecard(list, true)); + } else { + vector strvec = explode(db_read("Lists", search, "Value"), '\n'); + vector content; + for (int i = 0; i < strvec.size(); i++) + content.push_back( new Card( db_read("Cards", nameformat(strvec[i]), "Value").c_str() ) ); + list = new List( content ); + lists.push_back(list); + } + } else { + string url = "https://api.scryfall.com/cards/search?q=" + query; + list = new List(this, api_call(url), true); + lists.push_back(list); + string names = cachecard(list, false); + if (db_check("Lists", search)) { + string temp = nameformat( db_read("Lists", search, "Value") ); + db_write("Lists", search, names + "\n" + temp); + } else db_new("Lists", search, names); + } + + return list; +} + +Card * Scry::cards_named(string query) { + query = urlformat(query); + string url = "https://api.scryfall.com/cards/named?fuzzy=" + query; + Card * card = new Card(api_call(url)); + cards.push_back(card); + return card; +} + +Card * Scry::cards_named_cache(string query) { + query = urlformat(query); + Card * card; + query[0] = toupper(query[0]); + string name = nameformat(query); + + if (db_check("Cards", name)) { + if (datecheck( db_read("Cards", name, "Updated") ) == 1) { + card = cards_named(query); + db_write("Cards", name, nameformat(card->json())); + } else { + card = new Card( db_read("Cards", name, "Value").c_str() ); + cards.push_back(card); + } + } else { + card = cards_named(query); + db_new("Cards", name, nameformat(card->json())); + } + + return card; +} + +vector Scry::cards_autocomplete(string query) { + query = urlformat(query); + string url = "https://api.scryfall.com/cards/autocomplete?q=" + query; + Document doc; + doc.Parse(api_call(url)); + const Value& a = doc["data"]; + vector output; + for (auto& v : a.GetArray()) output.push_back(v.GetString()); + return output; +} + +vector Scry::cards_autocomplete_cache(string query) { + query = urlformat(query); + vector names; + + if (db_check("Autocompletes", query)) { + if (datecheck( db_read("Autocompletes", query, "Updated") ) == 1) { + names = cards_autocomplete(query); + string namestr = implode(names, '\n'); + db_write("Autocompletes", query, nameformat(namestr)); + } else { + names = explode(db_read("Autocompletes", query, "Value"), '\n'); + } + } else { + names = cards_autocomplete(query); + string namestr = implode(names, '\n'); + db_new("Autocompletes", query, nameformat(namestr)); + } + + return names; +} + +Card * Scry::cards_random() { + string url = "https://api.scryfall.com/cards/random"; + Card * card = new Card(api_call(url)); + cards.push_back(card); + return card; +} + +vector Scry::split(Card * card) { + Document doc; doc.Parse(card->json().c_str()); + vector output; + for (int i = 0; i < doc["card_faces"].Size(); i++) { + StringBuffer buffer; + Writer writer(buffer); + doc["card_faces"][i].Accept(writer); + Card * card = new Card(buffer.GetString()); + output.push_back(card); cards.push_back(card); + } + return output; +} + +Card::Card(const char * rawjson) { + data.Parse(rawjson); +} + +string Card::name() { + return data["name"].GetString(); +} + +string Card::mana_cost() { + if (!data.HasMember("mana_cost")) return ""; + return data["mana_cost"].GetString(); +} + +string Card::type_line() { + return data["type_line"].GetString(); +} + +string Card::oracle_text() { + if (!data.HasMember("oracle_text")) return ""; + return data["oracle_text"].GetString(); +} + +string Card::power() { + if (!data.HasMember("power")) return ""; + return data["power"].GetString(); +} + +string Card::toughness() { + if (!data.HasMember("toughness")) return ""; + return data["toughness"].GetString(); +} + +bool Card::dual_sided() { + return data.HasMember("card_faces"); +} + +string Card::json() { + StringBuffer buffer; + Writer writer(buffer); + data.Accept(writer); + return buffer.GetString(); +} + +List::List(Scry * scry, const char * rawjson, bool cache) { + Document doc; + doc.Parse(rawjson); + for (int i = 0; i < doc["data"].Size(); i++) { + StringBuffer buffer; + Writer writer(buffer); + doc["data"][i].Accept(writer); + content.push_back(new Card(buffer.GetString())); + } + if (doc["has_more"].GetBool()) { + string url = doc["next_page"].GetString(); + regex q("q=.*&"); + regex page("page=.*&q"); + smatch sm1; regex_search(url, sm1, q); + smatch sm2; regex_search(url, sm2, page); + string query = string(sm1[0]).substr(2, sm1[0].length()-2) + string(sm2[0]).substr(0, sm2[0].length()-2); + if (cache) nextpage = scry->cards_search_cache(query); + else nextpage = scry->cards_search(query); + } else nextpage = nullptr; +} + +List::List(vector input) { + content = input; + nextpage = nullptr; +} + +List::~List() { + while (!content.empty()) { + delete content.back(); + content.pop_back(); + } +} + +vector List::cards() { + return content; +} + +vector List::allcards() { + vector output = cards(); + if (nextpage != nullptr) { + vector append = nextpage->allcards(); + output.insert(output.end(), append.begin(), append.end()); + } + return output; +} diff --git a/libscry.h b/libscry.h new file mode 100644 index 0000000..e3c1b91 --- /dev/null +++ b/libscry.h @@ -0,0 +1,108 @@ +//libscry by Sebastien MacDougall-Landry +//License is available at +//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace rapidjson; +using namespace std::chrono; + +class Card; +class List; +class Scry; + +///This class is used to represent a single card +class Card { + public: + Card(const char * rawjson); + + ///Get the name (Storm Crow) of the card. + virtual string name(); + ///Get the mana cost ({1}{U}) of the card. + virtual string mana_cost(); + ///Get the type line (Creature - Bird) of the card. + virtual string type_line(); + ///Get the oracle text (Flying) of the card. + virtual string oracle_text(); + ///Get the power (1) of the card. + virtual string power(); + ///Get the toughness (2) of the card. + virtual string toughness(); + ///Returns true if the card has two faces, false otherwise. + virtual bool dual_sided(); + ///Get the raw json text of the card provided by Scryfall. + virtual string json(); + private: + Document data; +}; + +///This class is used to represent a list of cards (returned from a search for example) +class List { + public: + List(Scry * scry, const char * rawjson, bool cache); + List(vector input); + ~List(); + + ///Returns a vector with all the cards on this page of the list. For cards on all pages see allcards(). + virtual vector cards(); + ///Cumulates cards from all pages of the list and returns them as a vector. + virtual vector allcards(); + private: + vector content; + List * nextpage; +}; + +///This class is the main interface to Scryfall. It handles all the API requests and caching. +class Scry { + public: + Scry(); + ~Scry(); + + ///Put in a query using Scryfall syntax (commander:g+type:legendary) as the argument. + virtual List * cards_search(string query); + ///Cached version of cards_search. + virtual List * cards_search_cache(string query); + ///Put in the name of a card as the argument. + virtual Card * cards_named(string query); + ///Cached version of cards_named. + virtual Card * cards_named_cache(string query); + ///Put in part of a card's name as the argument. + virtual vector cards_autocomplete(string query); + ///Cached version of cards_autocomplete. + virtual vector cards_autocomplete_cache(string query); + ///Returns a randomly selected card. + virtual Card * cards_random(); + ///Splits a card into it's multiple faces. Useful because some cards won't have certain data in the conglomerate. + virtual vector split(Card * card); + private: + CURL *easyhandle; + sqlite3 *db; + vector cards; + vector lists; + virtual char * api_call(string url); + virtual string db_exec(string in); + virtual void db_init(string table); + virtual bool db_check(string table, string search); + virtual string db_read(string table, string search, string column); + virtual void db_write(string table, string key, string value); + virtual void db_new(string table, string key, string value); + virtual int datecheck(string datetime); + virtual const year_month_day parse(string datetime); + virtual vector explode(const string& str, const char& ch); + virtual string implode(const vector& strs, const char& ch); + virtual string urlformat(string str); + virtual string nameformat(string str); + virtual string cachecard(List * list, bool recursive); +}; diff --git a/makefile b/makefile index 733c16c..ff2412f 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ -build: src/*.cc - g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared src/*.cc -o libscry.so +build: libscry.h libscry.cc + g++ -std=c++20 -lcurl -lsqlite3 -fPIC -shared libscry.cc -o libscry.so install: libscry.h libscry.so @mkdir -p /usr/include/ diff --git a/src/card.cc b/src/card.cc deleted file mode 100644 index 08cc929..0000000 --- a/src/card.cc +++ /dev/null @@ -1,50 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#include "card.h" - -using namespace std; - -Card::Card(const char * rawjson) { - data.Parse(rawjson); -} - -string Card::name() { - return data["name"].GetString(); -} - -string Card::mana_cost() { - if (!data.HasMember("mana_cost")) return ""; - return data["mana_cost"].GetString(); -} - -string Card::type_line() { - return data["type_line"].GetString(); -} - -string Card::oracle_text() { - if (!data.HasMember("oracle_text")) return ""; - return data["oracle_text"].GetString(); -} - -string Card::power() { - if (!data.HasMember("power")) return ""; - return data["power"].GetString(); -} - -string Card::toughness() { - if (!data.HasMember("toughness")) return ""; - return data["toughness"].GetString(); -} - -bool Card::dual_sided() { - return data.HasMember("card_faces"); -} - -string Card::json() { - StringBuffer buffer; - Writer writer(buffer); - data.Accept(writer); - return buffer.GetString(); -} diff --git a/src/card.h b/src/card.h deleted file mode 100644 index ca7c23e..0000000 --- a/src/card.h +++ /dev/null @@ -1,36 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#pragma once -#include -#include -#include -#include - -using namespace std; -using namespace rapidjson; - -class Card { - public: - Card(const char * rawjson); - - ///Get the name (Storm Crow) of the card. - virtual string name(); - ///Get the mana cost ({1}{U}) of the card. - virtual string mana_cost(); - ///Get the type line (Creature - Bird) of the card. - virtual string type_line(); - ///Get the oracle text (Flying) of the card. - virtual string oracle_text(); - ///Get the power (1) of the card. - virtual string power(); - ///Get the toughness (2) of the card. - virtual string toughness(); - ///Returns true if the card has two faces, false otherwise. - virtual bool dual_sided(); - ///Get the raw json text of the card provided by Scryfall. - virtual string json(); - private: - Document data; -}; diff --git a/src/data.cc b/src/data.cc deleted file mode 100644 index a12720f..0000000 --- a/src/data.cc +++ /dev/null @@ -1,70 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#include "data.h" - -using namespace std; - -DataAccess::DataAccess() { - char * cachedir = getenv("XDG_CACHE_HOME"); - int rc; - if (cachedir != NULL) rc = sqlite3_open(strcat(cachedir, "/libscry.db"), &db); - else rc = sqlite3_open(strcat(getenv("HOME"), "/.cache/libscry.db"), &db); - if (rc) { - fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - exit(1); - } -} - -DataAccess::~DataAccess() { - sqlite3_close(db); -} - -string DataAccess::db_exec(string in) { - const char * cmd = in.c_str(); - sqlite3_stmt *stmt; - int rc = sqlite3_prepare_v2(db, cmd, -1, &stmt, NULL); - if (rc != SQLITE_OK) { - fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); - exit(1); - } - rc = sqlite3_step(stmt); - if (rc != SQLITE_ROW && rc != SQLITE_DONE) { - fprintf(stderr, "DB error: %s\n", sqlite3_errmsg(db)); - sqlite3_finalize(stmt); - exit(1); - } - string output; - string temp = in.substr(0, 6); - if (temp == "SELECT") output = string((char*)sqlite3_column_text(stmt, 0)); - sqlite3_finalize(stmt); - return output; -} - -void DataAccess::db_init(string table) { - db_exec("CREATE TABLE IF NOT EXISTS " + table + "(Key TEXT, Updated DATETIME, Value TEXT);"); -} - -bool DataAccess::db_check(string table, string search) { - string cmd = "SELECT COUNT(1) FROM " + table + " WHERE Key='" + search + "';"; - if (db_exec(cmd).compare("1") == 0) return true; - return false; -} - -string DataAccess::db_read(string table, string search, string column) { - string cmd = "SELECT " + column + " FROM " + table + " WHERE Key='" + search + "';"; - return db_exec(cmd); -} - -void DataAccess::db_write(string table, string key, string value) { - string cmd = "UPDATE " + table + " SET Updated=datetime(), Value='" + value + "' WHERE Key='" + key + "';"; - db_exec(cmd); -} - -void DataAccess::db_new(string table, string key, string value) { - string cmd = "INSERT INTO " + table + " VALUES ('" + key + "', datetime(), '" + value + "');"; - db_exec(cmd); -} - diff --git a/src/data.h b/src/data.h deleted file mode 100644 index 3502464..0000000 --- a/src/data.h +++ /dev/null @@ -1,27 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#pragma once -#include -#include -#include - -using namespace std; - -///This class is used to access the database -class DataAccess { - public: - DataAccess(); - ~DataAccess(); - - virtual void db_init(string table); - virtual bool db_check(string table, string search); - virtual string db_read(string table, string search, string column); - virtual void db_write(string table, string key, string value); - virtual void db_new(string table, string key, string value); - private: - sqlite3 *db; - virtual string db_exec(string in); -}; - diff --git a/src/list.cc b/src/list.cc deleted file mode 100644 index dd8bfdb..0000000 --- a/src/list.cc +++ /dev/null @@ -1,47 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#include "list.h" - -using namespace std; -using namespace rapidjson; - -List::List(const char * rawjson) { - Document doc; - doc.Parse(rawjson); - for (int i = 0; i < doc["data"].Size(); i++) { - StringBuffer buffer; - Writer writer(buffer); - doc["data"][i].Accept(writer); - content.push_back(new Card(buffer.GetString())); - } - if (doc["has_more"].GetBool()) { - string url = doc["next_page"].GetString(); - regex q("q=.*&"); - regex page("page=.*&q"); - smatch sm1; regex_search(url, sm1, q); - smatch sm2; regex_search(url, sm2, page); - nextpage = string(sm1[0]).substr(2, sm1[0].length()-2) + string(sm2[0]).substr(0, sm2[0].length()-2); - } else nextpage = ""; -} - -List::List(vector input) { - content = input; - nextpage = nullptr; -} - -List::~List() { - while (!content.empty()) { - delete content.back(); - content.pop_back(); - } -} - -vector List::cards() { - return content; -} - -string List::nextPage() { - return nextpage; -} diff --git a/src/list.h b/src/list.h deleted file mode 100644 index b3b070f..0000000 --- a/src/list.h +++ /dev/null @@ -1,28 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#pragma once -#include -#include -#include -#include -#include -#include "card.h" - -///This class is used to represent a list of cards (returned from a search for example) -class List { - public: - List(const char * rawjson); - List(vector input); - ~List(); - - ///Returns a vector with all the cards on this page of the list. For cards on all pages see allcards(). - virtual vector cards(); - ///Returns the string of the url for the next page of a search result - virtual string nextPage(); - private: - vector content; - string nextpage; -}; - diff --git a/src/scry.cc b/src/scry.cc deleted file mode 100644 index c93b107..0000000 --- a/src/scry.cc +++ /dev/null @@ -1,275 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#include "scry.h" - -using namespace std; -using namespace rapidjson; -using namespace std::chrono; - -extern "C" Scry* create_object() { - return new Scry; -} - -extern "C" void destroy_object( Scry* object ) { - delete object; -} - -Scry::Scry() { - wa = new WebAccess(); - da = new DataAccess(); - da->db_init("Cards"); - da->db_init("Lists"); - da->db_init("Autocompletes"); -} - -Scry::~Scry() { - delete(wa); - delete(da); - while (!cards.empty()) { - delete cards.back(); - cards.pop_back(); - } - while (!lists.empty()) { - delete lists.back(); - lists.pop_back(); - } -} - -const year_month_day Scry::parse(string datetime) { - string ys = datetime.substr(0,4); - string ms = datetime.substr(5,2); - string ds = datetime.substr(8,2); - year y(stoi(ys)); - month m (stoi(ms)); - day d (stoi(ds)); - const year_month_day output(y, m, d); - return output; -} - -int Scry::datecheck(string datetime) { - const time_point now{system_clock::now()}; - const year_month_day ymd{floor(now)}; - const year_month_day old = parse(datetime); - - int output; - if (static_cast(ymd.year()) > static_cast(old.year())) { - output = 1; - } else { - if (static_cast(ymd.month()) > static_cast(old.month())) { - output = 1; - } else { - if (static_cast(ymd.day()) > static_cast(old.day()) + 7) { - output = 1; - } else if (static_cast(ymd.day()) == static_cast(old.day()) + 7) { - output = 0; - } else { - output = -1; - } - } - } - - return output; -} - -vector Scry::explode(const string& str, const char& ch) { - string next; - vector result; - for (string::const_iterator it = str.begin(); it != str.end(); it++) { - if (*it == ch) { - if (!next.empty()) { - result.push_back(next); - next.clear(); - } - } else next += *it; - } - if (!next.empty()) result.push_back(next); - return result; -} - -string Scry::implode(const vector& strs, const char& ch) { - string result = ""; - for (auto it = strs.begin(); it != strs.end(); it++) { - result += (*it) + ch; - } - return result; -} - -string Scry::urlformat(string str) { - regex space(" "); - regex colon(":"); - str = regex_replace(str, space, "%20"); - str = regex_replace(str, colon, "%3A"); - return str; -} - -string Scry::nameformat(string str) { - regex apos("'"); - str = regex_replace(str, apos, "''"); - return str; -} - -List * Scry::cards_search(string query) { - query = urlformat(query); - string url = "https://api.scryfall.com/cards/search?q=" + query; - List * list = new List(wa->api_call(url)); - lists.push_back(list); - return list; -} - -string Scry::cachecard(List * list, bool recursive) { - string names = ""; - vector cards = allcards(list, true); - if (recursive) { - for (int i = 0; i < cards.size(); i++) { - string name = nameformat(cards[i]->name()); - names += name + "\n"; - string temp = nameformat(cards[i]->json()); - if (i < list->cards().size()) { - if (da->db_check("Cards", name)) { - da->db_write("Cards", name, temp); - } else da->db_new("Cards", name, temp); - } - } - } else { - for (int i = 0; i < cards.size(); i++) { - string name = nameformat(cards[i]->name()); - names += name + "\n"; - string temp = nameformat(cards[i]->json()); - if (da->db_check("Cards", name)) { - da->db_write("Cards", name, temp); - } else da->db_new("Cards", name, temp); - } - } - names.pop_back(); - return names; -} - -List * Scry::cards_search_cache(string query) { - query = urlformat(query); - List * list; - - regex pages(".*&p"); smatch sm; regex_search(query, sm, pages); - string search = string(sm[0]).substr(0, sm[0].length()-2); - if (size(search) < 1) search = query; - if (da->db_check("Lists", search)) { - if (datecheck( da->db_read("Lists", search, "Updated") ) == 1) { - string url = "https://api.scryfall.com/cards/search?q=" + query; - list = new List(wa->api_call(url)); - lists.push_back(list); - da->db_write("Lists", search, cachecard(list, true)); - } else { - vector strvec = explode(da->db_read("Lists", search, "Value"), '\n'); - vector content; - for (int i = 0; i < strvec.size(); i++) - content.push_back( new Card( da->db_read("Cards", nameformat(strvec[i]), "Value").c_str() ) ); - list = new List( content ); - lists.push_back(list); - } - } else { - string url = "https://api.scryfall.com/cards/search?q=" + query; - list = new List(wa->api_call(url)); - lists.push_back(list); - string names = cachecard(list, false); - if (da->db_check("Lists", search)) { - string temp = nameformat( da->db_read("Lists", search, "Value") ); - da->db_write("Lists", search, names + "\n" + temp); - } else da->db_new("Lists", search, names); - } - - return list; -} - -Card * Scry::cards_named(string query) { - query = urlformat(query); - string url = "https://api.scryfall.com/cards/named?fuzzy=" + query; - Card * card = new Card(wa->api_call(url)); - cards.push_back(card); - return card; -} - -Card * Scry::cards_named_cache(string query) { - query = urlformat(query); - Card * card; - query[0] = toupper(query[0]); - string name = nameformat(query); - - if (da->db_check("Cards", name)) { - if (datecheck( da->db_read("Cards", name, "Updated") ) == 1) { - card = cards_named(query); - da->db_write("Cards", name, nameformat(card->json())); - } else { - card = new Card( da->db_read("Cards", name, "Value").c_str() ); - cards.push_back(card); - } - } else { - card = cards_named(query); - da->db_new("Cards", name, nameformat(card->json())); - } - - return card; -} - -vector Scry::cards_autocomplete(string query) { - query = urlformat(query); - string url = "https://api.scryfall.com/cards/autocomplete?q=" + query; - Document doc; - doc.Parse(wa->api_call(url)); - const Value& a = doc["data"]; - vector output; - for (auto& v : a.GetArray()) output.push_back(v.GetString()); - return output; -} - -vector Scry::cards_autocomplete_cache(string query) { - query = urlformat(query); - vector names; - - if (da->db_check("Autocompletes", query)) { - if (datecheck( da->db_read("Autocompletes", query, "Updated") ) == 1) { - names = cards_autocomplete(query); - string namestr = implode(names, '\n'); - da->db_write("Autocompletes", query, nameformat(namestr)); - } else { - names = explode(da->db_read("Autocompletes", query, "Value"), '\n'); - } - } else { - names = cards_autocomplete(query); - string namestr = implode(names, '\n'); - da->db_new("Autocompletes", query, nameformat(namestr)); - } - - return names; -} - -Card * Scry::cards_random() { - string url = "https://api.scryfall.com/cards/random"; - Card * card = new Card(wa->api_call(url)); - cards.push_back(card); - return card; -} - -vector Scry::split(Card * card) { - Document doc; doc.Parse(card->json().c_str()); - vector output; - for (int i = 0; i < doc["card_faces"].Size(); i++) { - StringBuffer buffer; - Writer writer(buffer); - doc["card_faces"][i].Accept(writer); - Card * card = new Card(buffer.GetString()); - output.push_back(card); cards.push_back(card); - } - return output; -} - -vector Scry::allcards(List * list, bool cache) { - vector output = list->cards(); - if (list->nextPage() != "") { - vector append; - if (cache) append = allcards(cards_search_cache(list->nextPage()), true); - else append = allcards(cards_search(list->nextPage()), false); - output.insert(output.end(), append.begin(), append.end()); - } - return output; -} diff --git a/src/scry.h b/src/scry.h deleted file mode 100644 index f4c8599..0000000 --- a/src/scry.h +++ /dev/null @@ -1,54 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#pragma once -#include -#include -#include "web.h" -#include "data.h" -#include "list.h" -#include "card.h" - -using namespace std; -using namespace rapidjson; -using namespace std::chrono; - -///This class is used to represent a single card -///This class is the main interface to Scryfall. It handles all the API requests and caching. -class Scry { - public: - Scry(); - ~Scry(); - - ///Put in a query using Scryfall syntax (commander:g+type:legendary) as the argument. - virtual List * cards_search(string query); - ///Cached version of cards_search. - virtual List * cards_search_cache(string query); - ///Put in the name of a card as the argument. - virtual Card * cards_named(string query); - ///Cached version of cards_named. - virtual Card * cards_named_cache(string query); - ///Put in part of a card's name as the argument. - virtual vector cards_autocomplete(string query); - ///Cached version of cards_autocomplete. - virtual vector cards_autocomplete_cache(string query); - ///Returns a randomly selected card. - virtual Card * cards_random(); - ///Splits a card into it's multiple faces. Useful because some cards won't have certain data in the conglomerate. - virtual vector split(Card * card); - ///Returns all cards from a search using the list representing the first page - virtual vector allcards(List * list, bool cache); - private: - WebAccess * wa; - DataAccess * da; - vector cards; - vector lists; - virtual int datecheck(string datetime); - virtual const year_month_day parse(string datetime); - virtual vector explode(const string& str, const char& ch); - virtual string implode(const vector& strs, const char& ch); - virtual string urlformat(string str); - virtual string nameformat(string str); - virtual string cachecard(List * list, bool recursive); -}; diff --git a/src/web.cc b/src/web.cc deleted file mode 100644 index 576627b..0000000 --- a/src/web.cc +++ /dev/null @@ -1,53 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#include "web.h" - -using namespace std; - -struct memory { - char *response; - size_t size; -}; - -static size_t cb(void *data, size_t size, size_t nmemb, void *userp) { - size_t realsize = size * nmemb; - struct memory *mem = (struct memory *)userp; - - char *ptr = (char *)realloc(mem->response, mem->size + realsize + 1); - if (ptr == NULL) return 0; // out of memory! - - mem->response = ptr; - memcpy(&(mem->response[mem->size]), data, realsize); - mem->size += realsize; - mem->response[mem->size] = 0; - - return realsize; -} - -WebAccess::WebAccess() { - curl_global_init(CURL_GLOBAL_ALL); - easyhandle = curl_easy_init(); - curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, cb); - curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1); -} - -WebAccess::~WebAccess() { - curl_easy_cleanup(easyhandle); - curl_global_cleanup(); -} - -char * WebAccess::api_call(string url) { - curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str()); - struct memory chunk = {0}; - curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, (void *)&chunk); - CURLcode success = curl_easy_perform(easyhandle); - if (success != 0) { - fprintf(stderr, "Errored with CURLcode %i\n", success); - exit(success); - } - return chunk.response; -} - diff --git a/src/web.h b/src/web.h deleted file mode 100644 index 01d93e8..0000000 --- a/src/web.h +++ /dev/null @@ -1,20 +0,0 @@ -//libscry by Sebastien MacDougall-Landry -//License is available at -//https://github.com/EmperorPenguin18/libscry/blob/main/LICENSE - -#pragma once -#include -#include -#include - -using namespace std; - -class WebAccess { - public: - WebAccess(); - ~WebAccess(); - - virtual char * api_call(string url); - private: - CURL *easyhandle; -};