228 lines
6.9 KiB
C++
228 lines
6.9 KiB
C++
//
|
|
// Created by ricardo on 12/06/25.
|
|
//
|
|
#include <fstream>
|
|
#include "fetcher.h"
|
|
|
|
std::vector<LeetCodeProblem> Fetcher::getProblems() const
|
|
{
|
|
std::string body;
|
|
|
|
curl_easy_setopt(client.get(), CURLOPT_URL, kProblemsUrl.c_str());
|
|
curl_easy_setopt(client.get(), CURLOPT_WRITEFUNCTION, Fetcher::httpWriteCallback);
|
|
curl_easy_setopt(client.get(), CURLOPT_WRITEDATA, &body);
|
|
|
|
const CURLcode code = curl_easy_perform(client.get());
|
|
|
|
if (code != CURLE_OK)
|
|
{
|
|
std::cout << "Failed to fetch problems." << std::endl;
|
|
return {};
|
|
}
|
|
|
|
std::vector<LeetCodeProblem> problems;
|
|
|
|
try
|
|
{
|
|
nlohmann::json jsonResponse = nlohmann::json::parse(body);
|
|
|
|
for (const auto &item : jsonResponse["stat_status_pairs"])
|
|
{
|
|
bool paidOnly = item["paid_only"];
|
|
|
|
const auto &stat_item = item["stat"];
|
|
|
|
std::string frontendQuestionId = stat_item["frontend_question_id"];
|
|
int questionId = stat_item["question_id"];
|
|
std::string questionTitle = stat_item["question__title"];
|
|
std::string questionTitleSlug = stat_item["question__title_slug"];
|
|
|
|
problems.emplace_back(paidOnly, frontendQuestionId, questionId, questionTitle, questionTitleSlug);
|
|
}
|
|
}
|
|
catch (const nlohmann::json::parse_error &e)
|
|
{
|
|
std::cout << "JSON parse error: " << e.what() << std::endl;
|
|
std::cout << "Raw response: " << body << std::endl;
|
|
}
|
|
|
|
return problems;
|
|
}
|
|
|
|
std::string replaceString(const std::string &original,
|
|
const std::string_view old_sub,
|
|
const std::string_view new_sub)
|
|
{
|
|
if (old_sub.empty())
|
|
return original; // 防止空字符串导致死循环
|
|
|
|
std::string result;
|
|
size_t pos = 0;
|
|
size_t old_len = old_sub.size();
|
|
size_t new_len = new_sub.size();
|
|
|
|
// 预计算总长度以减少内存分配
|
|
size_t total_length = original.size();
|
|
size_t count = 0;
|
|
size_t start = 0;
|
|
|
|
while ((pos = original.find(old_sub, start)) != std::string::npos)
|
|
{
|
|
total_length += (new_len - old_len); // 更新总长度
|
|
count++;
|
|
start = pos + old_len; // 下一次查找起始位置
|
|
}
|
|
|
|
result.reserve(total_length); // 预分配内存
|
|
|
|
start = 0;
|
|
while ((pos = original.find(old_sub, start)) != std::string::npos)
|
|
{
|
|
// 添加旧子串之前的部分
|
|
result.append(original.data() + start, pos - start);
|
|
// 添加新子串
|
|
result.append(new_sub.data(), new_sub.size());
|
|
// 更新起始位置
|
|
start = pos + old_len;
|
|
}
|
|
|
|
// 添加剩余部分
|
|
result.append(original.data() + start, original.size() - start);
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string ProblemContent::formatTemplate(const std::string &templateContent) const
|
|
{
|
|
const auto it = std::ranges::find_if(codeDefinitions,
|
|
[](const CodeDefinition &definition)
|
|
{
|
|
return definition.value == "cpp";
|
|
});
|
|
|
|
if (it == codeDefinitions.end())
|
|
{
|
|
throw std::runtime_error("The target problem has no C++ template.");
|
|
}
|
|
|
|
std::string result = replaceString(templateContent, "__PROBLEM_ID__", std::to_string(questionId));
|
|
result = replaceString(result, "__PROBLEM_TITLE__", title);
|
|
result = replaceString(result, "__PROBLEM_DEFAULT_CODE__", it->defaultCode);
|
|
|
|
return result;
|
|
}
|
|
|
|
void Fetcher::fetchProblem(const std::string &idString) const
|
|
{
|
|
std::vector<LeetCodeProblem> problems = getProblems();
|
|
|
|
const auto it = std::ranges::find_if(problems,
|
|
[idString](const LeetCodeProblem &problem)
|
|
{
|
|
return problem.frontendQuestionId == idString;
|
|
});
|
|
|
|
if (it == problems.end())
|
|
{
|
|
throw std::runtime_error("The target problem does not exist.");
|
|
}
|
|
|
|
curl_easy_setopt(client.get(), CURLOPT_URL, kGraphQlUrl.c_str());
|
|
curl_easy_setopt(client.get(), CURLOPT_POST, 1L);
|
|
|
|
curl_slist *headers = nullptr;
|
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
|
|
|
curl_easy_setopt(client.get(), CURLOPT_HTTPHEADER, headers);
|
|
|
|
std::string requestBody = formatQueryJson(it->questionTitleSlug).dump();
|
|
|
|
curl_easy_setopt(client.get(), CURLOPT_POSTFIELDS, requestBody.c_str());
|
|
curl_easy_setopt(client.get(), CURLOPT_POSTFIELDSIZE, requestBody.size());
|
|
|
|
std::string responseBody;
|
|
|
|
curl_easy_setopt(client.get(), CURLOPT_WRITEFUNCTION, Fetcher::httpWriteCallback);
|
|
curl_easy_setopt(client.get(), CURLOPT_WRITEDATA, &responseBody);
|
|
|
|
CURLcode code = curl_easy_perform(client.get());
|
|
|
|
if (code != CURLE_OK)
|
|
{
|
|
throw std::runtime_error("Failed to fetch problem.");
|
|
}
|
|
|
|
const nlohmann::json jsonResponse = nlohmann::json::parse(responseBody);
|
|
const std::unique_ptr<ProblemContent> problemContent = extractContentFromJson(jsonResponse, *it);
|
|
|
|
std::string templateFile = readTemplateFile();
|
|
|
|
std::cout << problemContent->formatTemplate(templateFile) << std::endl;
|
|
}
|
|
|
|
nlohmann::json Fetcher::formatQueryJson(const std::string &title)
|
|
{
|
|
nlohmann::json result;
|
|
|
|
result["operationName"] = "questionData";
|
|
result["query"] = R"(query questionData($titleSlug: String!) {
|
|
question(titleSlug: $titleSlug) {
|
|
content
|
|
stats
|
|
codeDefinition
|
|
sampleTestCase
|
|
}
|
|
})";
|
|
|
|
nlohmann::json variables;
|
|
variables["titleSlug"] = title;
|
|
result["variables"] = variables;
|
|
|
|
return result;
|
|
}
|
|
|
|
std::unique_ptr<ProblemContent> Fetcher::extractContentFromJson(const nlohmann::json &json, const LeetCodeProblem &problem)
|
|
{
|
|
const auto questionJson = json["data"]["question"];
|
|
std::string content = questionJson["content"];
|
|
|
|
std::vector<CodeDefinition> codeDefinitions;
|
|
const std::string codeDefinitionString = questionJson["codeDefinition"];
|
|
|
|
for (nlohmann::json codeDefinitionJson = nlohmann::json::parse(codeDefinitionString);
|
|
const auto &codeDefinitionItem : codeDefinitionJson)
|
|
{
|
|
std::string value = codeDefinitionItem["value"];
|
|
std::string text = codeDefinitionItem["text"];
|
|
std::string defaultCode = codeDefinitionItem["defaultCode"];
|
|
|
|
codeDefinitions.emplace_back(value, text, defaultCode);
|
|
}
|
|
|
|
return std::make_unique<ProblemContent>(
|
|
problem.questionTitle,
|
|
problem.questionTitleSlug,
|
|
content,
|
|
codeDefinitions,
|
|
std::stoi(problem.frontendQuestionId));
|
|
}
|
|
|
|
std::string Fetcher::readTemplateFile()
|
|
{
|
|
std::ifstream file;
|
|
file.open("src/template.cpp");
|
|
|
|
std::stringstream buffer;
|
|
if (file.is_open())
|
|
{
|
|
buffer << file.rdbuf();
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Failed to read template file.");
|
|
}
|
|
|
|
file.close();
|
|
|
|
return buffer.str();
|
|
} |