feat: develop the basic functions.
This commit is contained in:
parent
9ddcad2f08
commit
52d9629a5c
|
@ -110,8 +110,8 @@ IndentGotoLabels: false
|
||||||
# #endif
|
# #endif
|
||||||
# #endif
|
# #endif
|
||||||
IndentPPDirectives: BeforeHash
|
IndentPPDirectives: BeforeHash
|
||||||
# IndentAccessModifiers: true
|
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
|
AccessModifierOffset: -4
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
MaxEmptyLinesToKeep: 3
|
MaxEmptyLinesToKeep: 3
|
||||||
NamespaceIndentation: None
|
NamespaceIndentation: None
|
||||||
|
|
|
@ -6,6 +6,9 @@ include_directories(include)
|
||||||
|
|
||||||
find_package(CURL REQUIRED)
|
find_package(CURL REQUIRED)
|
||||||
find_package(nlohmann_json REQUIRED)
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
find_package(GTest REQUIRED)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
add_executable(leetcode-fetcher main.cpp
|
add_executable(leetcode-fetcher main.cpp
|
||||||
src/fetcher.cpp)
|
src/fetcher.cpp)
|
||||||
|
|
|
@ -59,25 +59,27 @@ struct CodeDefinition
|
||||||
struct ProblemContent
|
struct ProblemContent
|
||||||
{
|
{
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string title_slug;
|
std::string titleSlug;
|
||||||
std::string content;
|
std::string content;
|
||||||
std::vector<CodeDefinition> codeDefinitions;
|
std::vector<CodeDefinition> codeDefinitions;
|
||||||
int questionId;
|
int questionId;
|
||||||
|
|
||||||
ProblemContent(std::string title,
|
ProblemContent(std::string title,
|
||||||
std::string title_slug,
|
std::string titleSlug,
|
||||||
std::string content,
|
std::string content,
|
||||||
std::vector<CodeDefinition> codeDefinitions,
|
std::vector<CodeDefinition> codeDefinitions,
|
||||||
int questionId)
|
int questionId)
|
||||||
: title(std::move(title))
|
: title(std::move(title))
|
||||||
, title_slug(std::move(title_slug))
|
, titleSlug(std::move(titleSlug))
|
||||||
, content(std::move(content))
|
, content(std::move(content))
|
||||||
, codeDefinitions(std::move(codeDefinitions))
|
, codeDefinitions(std::move(codeDefinitions))
|
||||||
, questionId(questionId)
|
, questionId(questionId)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string formatTemplate(const std::string &templateContent) const;
|
[[nodiscard]] std::string formatTemplate(const std::string &templateContent) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::string formatFilename() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Fetcher
|
struct Fetcher
|
||||||
|
@ -104,6 +106,8 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] std::vector<LeetCodeProblem> getProblems() const;
|
[[nodiscard]] std::vector<LeetCodeProblem> getProblems() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<ProblemContent> fetchProblemContent(const LeetCodeProblem &problem) const;
|
||||||
|
|
||||||
/// The callback function used by curl to write the http response content into a string.
|
/// 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)
|
static size_t httpWriteCallback(void *contents, const size_t size, size_t bufferLength, void *userData)
|
||||||
{
|
{
|
||||||
|
@ -121,6 +125,8 @@ private:
|
||||||
const LeetCodeProblem &problem);
|
const LeetCodeProblem &problem);
|
||||||
|
|
||||||
static std::string readTemplateFile();
|
static std::string readTemplateFile();
|
||||||
|
|
||||||
|
static bool validateExistedProblem(const ProblemContent &problem);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FETCHER_H
|
#endif //FETCHER_H
|
31
justfile
Normal file
31
justfile
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env just --justfile
|
||||||
|
|
||||||
|
update:
|
||||||
|
git pull
|
||||||
|
|
||||||
|
build: update
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euxo pipefail
|
||||||
|
mkdir -p cmake-build-debug-clang
|
||||||
|
cd cmake-build-debug-clang
|
||||||
|
cmake .. -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||||
|
ninja
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
clang-format -i src/problems/*.cpp
|
||||||
|
|
||||||
|
test: fmt build
|
||||||
|
./cmake-build-debug-clang/src/problem-tests
|
||||||
|
|
||||||
|
commit: test
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euxo pipefail
|
||||||
|
time=$(date "+%Y%m%d")
|
||||||
|
message="$time finished."
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
git commit -m "$message"
|
||||||
|
git push
|
||||||
|
|
||||||
|
pull id: build
|
||||||
|
./cmake-build-debug-clang/leetcode-fetcher {{ id }}
|
3
main.cpp
3
main.cpp
|
@ -6,8 +6,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
{
|
{
|
||||||
std::cout << "The fetcher expect the program id.";
|
throw std::invalid_argument("The fetcher expect the program id.");
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetcher.fetchProblem(argv[1]);
|
fetcher.fetchProblem(argv[1]);
|
||||||
|
|
7
src/CMakeLists.txt
Normal file
7
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/problems PROBLEMS_SRC)
|
||||||
|
|
||||||
|
add_executable(problem-tests ${PROBLEMS_SRC})
|
||||||
|
|
||||||
|
target_link_libraries(problem-tests GTest::gtest_main)
|
100
src/fetcher.cpp
100
src/fetcher.cpp
|
@ -49,7 +49,38 @@ std::vector<LeetCodeProblem> Fetcher::getProblems() const
|
||||||
return problems;
|
return problems;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string replaceString(const std::string &original,
|
std::unique_ptr<ProblemContent> Fetcher::fetchProblemContent(const LeetCodeProblem &problem) const
|
||||||
|
{
|
||||||
|
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(problem.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);
|
||||||
|
return extractContentFromJson(jsonResponse, problem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string replaceString(const std::string &original,
|
||||||
const std::string_view old_sub,
|
const std::string_view old_sub,
|
||||||
const std::string_view new_sub)
|
const std::string_view new_sub)
|
||||||
{
|
{
|
||||||
|
@ -108,10 +139,21 @@ std::string ProblemContent::formatTemplate(const std::string &templateContent) c
|
||||||
std::string result = replaceString(templateContent, "__PROBLEM_ID__", std::to_string(questionId));
|
std::string result = replaceString(templateContent, "__PROBLEM_ID__", std::to_string(questionId));
|
||||||
result = replaceString(result, "__PROBLEM_TITLE__", title);
|
result = replaceString(result, "__PROBLEM_TITLE__", title);
|
||||||
result = replaceString(result, "__PROBLEM_DEFAULT_CODE__", it->defaultCode);
|
result = replaceString(result, "__PROBLEM_DEFAULT_CODE__", it->defaultCode);
|
||||||
|
std::string testCaseName = "P" + std::to_string(questionId);
|
||||||
|
result = replaceString(result, "__TEST_CASE_NAME__", testCaseName);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ProblemContent::formatFilename() const
|
||||||
|
{
|
||||||
|
std::ostringstream stream("p");
|
||||||
|
stream << questionId;
|
||||||
|
stream << "-" << titleSlug << ".cpp";
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
void Fetcher::fetchProblem(const std::string &idString) const
|
void Fetcher::fetchProblem(const std::string &idString) const
|
||||||
{
|
{
|
||||||
std::vector<LeetCodeProblem> problems = getProblems();
|
std::vector<LeetCodeProblem> problems = getProblems();
|
||||||
|
@ -127,37 +169,21 @@ void Fetcher::fetchProblem(const std::string &idString) const
|
||||||
throw std::runtime_error("The target problem does not exist.");
|
throw std::runtime_error("The target problem does not exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_setopt(client.get(), CURLOPT_URL, kGraphQlUrl.c_str());
|
std::unique_ptr<ProblemContent> problemContent = fetchProblemContent(*it);
|
||||||
curl_easy_setopt(client.get(), CURLOPT_POST, 1L);
|
const std::string templateFile = readTemplateFile();
|
||||||
|
std::string problemFileContent = problemContent->formatTemplate(templateFile);
|
||||||
|
std::string problemFilename = problemContent->formatFilename();
|
||||||
|
|
||||||
curl_slist *headers = nullptr;
|
auto problemFile = std::ofstream("src/problems/" + problemFilename);
|
||||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
|
||||||
|
|
||||||
curl_easy_setopt(client.get(), CURLOPT_HTTPHEADER, headers);
|
if (!problemFile.is_open())
|
||||||
|
|
||||||
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.");
|
throw std::runtime_error("Failed to open problem file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const nlohmann::json jsonResponse = nlohmann::json::parse(responseBody);
|
problemFile << problemFileContent;
|
||||||
const std::unique_ptr<ProblemContent> problemContent = extractContentFromJson(jsonResponse, *it);
|
|
||||||
|
|
||||||
std::string templateFile = readTemplateFile();
|
problemFile.close();
|
||||||
|
|
||||||
std::cout << problemContent->formatTemplate(templateFile) << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json Fetcher::formatQueryJson(const std::string &title)
|
nlohmann::json Fetcher::formatQueryJson(const std::string &title)
|
||||||
|
@ -226,3 +252,25 @@ std::string Fetcher::readTemplateFile()
|
||||||
|
|
||||||
return buffer.str();
|
return buffer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Fetcher::validateExistedProblem(const ProblemContent &problem)
|
||||||
|
{
|
||||||
|
std::filesystem::path problemDirectory = "src/problems";
|
||||||
|
std::string defaultFilename = problem.formatFilename();
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(problemDirectory))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("The problem directory is not exitsed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::ranges::any_of(std::filesystem::directory_iterator(problemDirectory),
|
||||||
|
[&](const std::filesystem::directory_entry &entry)
|
||||||
|
{
|
||||||
|
if (!entry.is_regular_file())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.path().filename() == defaultFilename;
|
||||||
|
});
|
||||||
|
}
|
40
src/problems/1-two-sum.cpp
Normal file
40
src/problems/1-two-sum.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* [1] Two Sum
|
||||||
|
*/
|
||||||
|
#include <bits/stdc++.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
// submission codes start here
|
||||||
|
|
||||||
|
class Solution
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
vector<int> twoSum(vector<int> &nums, int target)
|
||||||
|
{
|
||||||
|
unordered_map<int, int> map;
|
||||||
|
|
||||||
|
for (int i = 0; i < nums.size(); ++i)
|
||||||
|
{
|
||||||
|
if (const auto &it = map.find(target - nums[i]); it != map.end())
|
||||||
|
{
|
||||||
|
return {it->second, i};
|
||||||
|
}
|
||||||
|
|
||||||
|
map.insert({nums[i], i});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// submission codes endo
|
||||||
|
|
||||||
|
TEST(P1, Test1)
|
||||||
|
{
|
||||||
|
Solution s;
|
||||||
|
vector nums = {2, 7, 11, 15};
|
||||||
|
vector result = {0, 1};
|
||||||
|
ASSERT_EQ(s.twoSum(nums, 9), result);
|
||||||
|
}
|
|
@ -1,9 +1,18 @@
|
||||||
/**
|
/**
|
||||||
* [__PROBLEM_ID__] __PROBLEM_TITLE__
|
* [__PROBLEM_ID__] __PROBLEM_TITLE__
|
||||||
*/
|
*/
|
||||||
|
#include <bits/stdc++.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
// submission codes start here
|
// submission codes start here
|
||||||
|
|
||||||
__PROBLEM_DEFAULT_CODE__
|
__PROBLEM_DEFAULT_CODE__
|
||||||
|
|
||||||
// submission codes end
|
// submission codes endo
|
||||||
|
|
||||||
|
TEST(__TEST_CASE_NAME__, Test1)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user