82 lines
2.7 KiB
C++
82 lines
2.7 KiB
C++
#include <ctime>
|
|
#include <regex>
|
|
#include <stdexcept>
|
|
#include <system_error>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include "models.h"
|
|
#include "numberhelper.h"
|
|
|
|
using json = nlohmann::json;
|
|
static time_t parse_rfc3339(const std::string& str);
|
|
|
|
|
|
void from_json(const json& j, Emoji& emoji) {
|
|
j.at("shortcode").get_to(emoji.shortcode);
|
|
j.at("url").get_to(emoji.url);
|
|
}
|
|
|
|
void from_json(const json& j, AccountField& field) {
|
|
j.at("name").get_to(field.name);
|
|
field.value_html = j.at("value").get<std::string>();
|
|
if (j.at("verified_at").is_null()) {
|
|
field.verified_at = -1;
|
|
} else {
|
|
field.verified_at = parse_rfc3339(j["verified_at"].get_ref<const std::string&>());
|
|
}
|
|
}
|
|
|
|
static std::regex host_regex(R"EOF(https?://([a-z0-9-.]+)/.+)EOF", std::regex::ECMAScript | std::regex::icase);
|
|
void from_json(const json& j, Account& account) {
|
|
using namespace std::string_literals;
|
|
|
|
j.at("id").get_to(account.id);
|
|
j.at("username").get_to(account.username);
|
|
j.at("display_name").get_to(account.display_name);
|
|
account.created_at = parse_rfc3339(j.at("created_at").get_ref<const std::string&>());
|
|
account.note_html = j.at("note").get<std::string>();
|
|
j.at("avatar").get_to(account.avatar);
|
|
j.at("header").get_to(account.header);
|
|
j.at("followers_count").get_to(account.followers_count);
|
|
j.at("following_count").get_to(account.following_count);
|
|
j.at("statuses_count").get_to(account.statuses_count);
|
|
j.at("emojis").get_to(account.emojis);
|
|
j.at("fields").get_to(account.fields);
|
|
|
|
std::smatch sm;
|
|
const std::string& url = j.at("url").get_ref<const std::string&>();
|
|
if (!std::regex_match(url, sm, host_regex)) {
|
|
throw std::runtime_error("failed to find host in url: "s + url);
|
|
}
|
|
account.domain_name = sm.str(1);
|
|
}
|
|
|
|
|
|
static std::regex rfc3339_re(R"EOF((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:(Z)|([+-]\d{2}):(\d{2})))EOF", std::regex::ECMAScript | std::regex::icase);
|
|
time_t parse_rfc3339(const std::string& str) {
|
|
using namespace std::string_literals;
|
|
|
|
std::smatch sm;
|
|
if (!std::regex_match(str, sm, rfc3339_re)) {
|
|
throw std::invalid_argument("unknown date format: "s + str);
|
|
}
|
|
|
|
struct tm tm = {
|
|
.tm_sec = to_int(sm.str(6)),
|
|
.tm_min = to_int(sm.str(5)),
|
|
.tm_hour = to_int(sm.str(4)),
|
|
|
|
.tm_mday = to_int(sm.str(3)),
|
|
.tm_mon = to_int(sm.str(2)) - 1,
|
|
.tm_year = to_int(sm.str(1)) - 1900,
|
|
|
|
.tm_isdst = -1,
|
|
.tm_gmtoff = !sm.str(7).empty() ? 0 : to_int(sm.str(8)) * 60 * 60 + to_int(sm.str(9)) * 60,
|
|
};
|
|
time_t time = mktime(&tm);
|
|
if (time == -1) {
|
|
throw std::system_error(errno, std::generic_category(), "mktime()");
|
|
}
|
|
return time;
|
|
}
|