init repo.

This commit is contained in:
jackfiled 2025-06-12 22:23:50 +08:00
commit 9ddcad2f08
Signed by: jackfiled
GPG Key ID: DEF448811AE0286D
7 changed files with 559 additions and 0 deletions

163
.clang-format Normal file
View File

@ -0,0 +1,163 @@
# public, protected, private 修饰符对齐
# AccessModifierOffset: 2
# 长函数调用时,参数对齐, 括号形式
# someLongFunction(
# argument1, argument2
# )
#
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
# 连续的赋值语句对齐
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: true
PadOperators: true
#AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: None
AlignEscapedNewlines: Right
# Align
# x = aaaaaaaa +
# bbbbbbbb
#
# when BreakBeforeBinaryOperators is set
#
# x = aaaaaaaa +
# bbbbbbbb
#
# AlignAfterOperator
# x = aaaaaaaa
# + bbbbbbbb
#AlignOperands: AlignAfterOperator
AlignTrailingComments:
Kind: Always
OverEmptyLines: 2
# true:
# callFunction(
# a, b, c, d);
#
# false:
# callFunction(a,
# b,
# c,
# d);
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
#AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
# 在template声明时是否换行
# template <typename T>
# T foo() {
# }
# template <typename T>
# T foo(int aaaaaaaaaaaaaaaaaaaaa,
# int bbbbbbbbbbbbbbbbbbbbb) {
# }
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
#BitFieldColonSpacing: Both
BreakBeforeBraces: "Allman"
# true:
# veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongDescription
# ? firstValue
# : SecondValueVeryVeryVeryVeryLong;
#
# false:
# veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongDescription ?
# firstValue :
# SecondValueVeryVeryVeryVeryLong;
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: false
ColumnLimit: 0 # 0: no limit
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
Cpp11BracedListStyle: true
FixNamespaceComments: true # 加上丢失的namespace注释
IncludeBlocks: Preserve
#IndentCaseBlocks: false
IndentCaseLabels: true
IndentGotoLabels: false
# #if FOO
# #if BAR
# #include <foo>
# #endif
# #endif
IndentPPDirectives: BeforeHash
# IndentAccessModifiers: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 3
NamespaceIndentation: None
# Left:
# int* a;
# Right:
# int *a;
# Middle:
# int * a;
PointerAlignment: Right
# QualifierOrder
ReferenceAlignment: Right
# 按照列数限制, 将注释进行换行
ReflowComments: false
SortIncludes: CaseSensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCaseColon: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 4
SpacesInAngles: Leave
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
# Constructor
ConstructorInitializerAllOnOneLineOrOnePerLine: true
Standard: c++20
TabWidth: 4
UseTab: Never

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea/
build/
cmake-build-*/

14
CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.20)
project(leetcode-cpp)
set(CMAKE_CXX_STANDARD 20)
include_directories(include)
find_package(CURL REQUIRED)
find_package(nlohmann_json REQUIRED)
add_executable(leetcode-fetcher main.cpp
src/fetcher.cpp)
target_link_libraries(leetcode-fetcher PRIVATE CURL::libcurl)
target_link_libraries(leetcode-fetcher PRIVATE nlohmann_json::nlohmann_json)

126
include/fetcher.h Normal file
View File

@ -0,0 +1,126 @@
//
// Created by ricardo on 12/06/25.
//
#ifndef FETCHER_H
#define FETCHER_H
#include <iostream>
#include <memory>
#include <curl/curl.h>
#include <nlohmann/json.hpp>
struct CurlDeleter
{
void operator()(CURL *curl) const
{
if (curl != nullptr)
{
curl_easy_cleanup(curl);
}
}
};
struct LeetCodeProblem
{
LeetCodeProblem(bool paidOnly,
std::string frontendQuestionId,
int questionId,
std::string questionTitle,
std::string questionTitleSlug)
: paidOnly(paidOnly)
, frontendQuestionId(std::move(frontendQuestionId))
, questionId(questionId)
, questionTitle(std::move(questionTitle))
, questionTitleSlug(std::move(questionTitleSlug))
{
}
bool paidOnly;
std::string frontendQuestionId;
int questionId;
std::string questionTitle;
std::string questionTitleSlug;
};
struct CodeDefinition
{
std::string value;
std::string text;
std::string defaultCode;
CodeDefinition(std::string value, std::string text, std::string defaultCode)
: value(std::move(value))
, text(std::move(text))
, defaultCode(std::move(defaultCode))
{
}
};
struct ProblemContent
{
std::string title;
std::string title_slug;
std::string content;
std::vector<CodeDefinition> codeDefinitions;
int questionId;
ProblemContent(std::string title,
std::string title_slug,
std::string content,
std::vector<CodeDefinition> codeDefinitions,
int questionId)
: title(std::move(title))
, title_slug(std::move(title_slug))
, content(std::move(content))
, codeDefinitions(std::move(codeDefinitions))
, questionId(questionId)
{
}
std::string formatTemplate(const std::string &templateContent) const;
};
struct Fetcher
{
Fetcher()
{
// Initialize the cURL global environment.
curl_global_init(CURL_GLOBAL_ALL);
client = std::unique_ptr<CURL, CurlDeleter>(curl_easy_init());
}
~Fetcher()
{
curl_global_cleanup();
}
void fetchProblem(const std::string &idString) const;
private:
std::unique_ptr<CURL, CurlDeleter> client;
std::string kProblemsUrl = "https://leetcode.cn/api/problems/algorithms/";
std::string kGraphQlUrl = "https://leetcode.cn/graphql";
[[nodiscard]] std::vector<LeetCodeProblem> getProblems() const;
/// The callback function used by curl to write the http response content into a string.
static size_t httpWriteCallback(void *contents, const size_t size, size_t bufferLength, void *userData)
{
auto string = static_cast<std::string *>(userData);
auto body = static_cast<char *>(contents);
string->append(body, size * bufferLength);
return size * bufferLength;
}
static nlohmann::json formatQueryJson(const std::string &title);
static std::unique_ptr<ProblemContent> extractContentFromJson(
const nlohmann::json &json,
const LeetCodeProblem &problem);
static std::string readTemplateFile();
};
#endif //FETCHER_H

16
main.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "fetcher.h"
int main(int argc, char **argv)
{
Fetcher fetcher;
if (argc != 2)
{
std::cout << "The fetcher expect the program id.";
return -1;
}
fetcher.fetchProblem(argv[1]);
return 0;
}

228
src/fetcher.cpp Normal file
View File

@ -0,0 +1,228 @@
//
// 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();
}

9
src/template.cpp Normal file
View File

@ -0,0 +1,9 @@
/**
* [__PROBLEM_ID__] __PROBLEM_TITLE__
*/
// submission codes start here
__PROBLEM_DEFAULT_CODE__
// submission codes end