// // Created by ricardo on 12/06/25. // #include #include "fetcher.h" std::vector 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 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 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 = 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 Fetcher::extractContentFromJson(const nlohmann::json &json, const LeetCodeProblem &problem) { const auto questionJson = json["data"]["question"]; std::string content = questionJson["content"]; std::vector 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( 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(); }