From 464e9b33884618ee6f4e2298d8d9ce86ee2ea9ec Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Thu, 11 Jul 2024 02:41:29 +0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20gucc:=20add=20helper=20function?= =?UTF-8?q?=20to=20fetch=20file=20from=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 2 ++ gucc/CMakeLists.txt | 3 ++- gucc/include/gucc/fetch_file.hpp | 15 +++++++++++ gucc/meson.build | 1 + gucc/src/fetch_file.cpp | 45 ++++++++++++++++++++++++++++++++ gucc/tests/CMakeLists.txt | 4 ++- gucc/tests/meson.build | 8 ++++++ gucc/tests/unit-fetch_file.cpp | 45 ++++++++++++++++++++++++++++++++ meson.build | 1 + 9 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 gucc/include/gucc/fetch_file.hpp create mode 100644 gucc/src/fetch_file.cpp create mode 100644 gucc/tests/unit-fetch_file.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e38b1d1..fd720f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,6 +42,7 @@ jobs: ./gucc/tests/test-fstab_gen ./gucc/tests/test-crypttab_gen ./gucc/tests/test-grub_config_gen + ./gucc/tests/test-fetch_file shell: bash build-cmake_withoutdev: name: Build with CMake (DEVENV OFF) @@ -90,6 +91,7 @@ jobs: ./gucc/tests/test-fstab_gen ./gucc/tests/test-crypttab_gen ./gucc/tests/test-grub_config_gen + ./gucc/tests/test-fetch_file shell: bash build-meson_withoutdev: name: Build with Meson (DEVENV OFF) diff --git a/gucc/CMakeLists.txt b/gucc/CMakeLists.txt index 8295c1b..efc7739 100644 --- a/gucc/CMakeLists.txt +++ b/gucc/CMakeLists.txt @@ -32,13 +32,14 @@ add_library(${PROJECT_NAME} #SHARED src/hwclock.cpp include/gucc/hwclock.hpp src/package_profiles.cpp include/gucc/package_profiles.hpp src/logger.cpp include/gucc/logger.hpp + src/fetch_file.cpp include/gucc/fetch_file.hpp #src/chwd_profiles.cpp src/chwd_profiles.hpp #src/disk.cpp src/disk.hpp ) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_DIR}/include) -target_link_libraries(${PROJECT_NAME} PUBLIC project_warnings project_options spdlog::spdlog fmt::fmt range-v3::range-v3 tomlplusplus::tomlplusplus) +target_link_libraries(${PROJECT_NAME} PUBLIC project_warnings project_options spdlog::spdlog fmt::fmt range-v3::range-v3 tomlplusplus::tomlplusplus cpr::cpr) if(COS_INSTALLER_BUILD_TESTS) add_subdirectory(tests) diff --git a/gucc/include/gucc/fetch_file.hpp b/gucc/include/gucc/fetch_file.hpp new file mode 100644 index 0000000..4904240 --- /dev/null +++ b/gucc/include/gucc/fetch_file.hpp @@ -0,0 +1,15 @@ +#ifndef FETCH_FILE_HPP +#define FETCH_FILE_HPP + +#include // for optional +#include // for string +#include // for string_view + +namespace gucc::fetch { + +// Fetch file from url into memory +auto fetch_file_from_url(std::string_view url, std::string_view fallback_url) noexcept -> std::optional; + +} // namespace gucc::fetch + +#endif // FETCH_FILE_HPP diff --git a/gucc/meson.build b/gucc/meson.build index 83b0ff8..7421064 100644 --- a/gucc/meson.build +++ b/gucc/meson.build @@ -22,6 +22,7 @@ gucc_lib = library('gucc', 'src/hwclock.cpp', 'src/package_profiles.cpp', 'src/logger.cpp', + 'src/fetch_file.cpp', ], include_directories : [include_directories('include')], dependencies: deps diff --git a/gucc/src/fetch_file.cpp b/gucc/src/fetch_file.cpp new file mode 100644 index 0000000..7ef1177 --- /dev/null +++ b/gucc/src/fetch_file.cpp @@ -0,0 +1,45 @@ +#include "gucc/fetch_file.hpp" + +#include // for chrono_literals + +#include +#include +#include +#include + +using namespace std::string_view_literals; + +namespace { + +auto fetch_file(std::string_view url) noexcept -> std::optional { + using namespace std::chrono_literals; + + auto timeout = cpr::Timeout{30s}; + auto response = cpr::Get(cpr::Url{url}, timeout); + auto status_code = static_cast(response.status_code); + + static constexpr auto FILE_URL_PREFIX = "file://"sv; + if (cpr::status::is_success(status_code) || (url.starts_with(FILE_URL_PREFIX) && status_code == 0 && !response.text.empty())) { + return std::make_optional(std::move(response.text)); + } + return std::nullopt; +} + +} // namespace + +namespace gucc::fetch { + +auto fetch_file_from_url(std::string_view url, std::string_view fallback_url) noexcept -> std::optional { + auto fetch_content = fetch_file(url); + if (fetch_content) { + return std::make_optional(std::move(*fetch_content)); + } + + fetch_content = fetch_file(fallback_url); + if (fetch_content) { + return std::make_optional(std::move(*fetch_content)); + } + return std::nullopt; +} + +} // namespace gucc::fetch diff --git a/gucc/tests/CMakeLists.txt b/gucc/tests/CMakeLists.txt index 63d7f63..c960d52 100644 --- a/gucc/tests/CMakeLists.txt +++ b/gucc/tests/CMakeLists.txt @@ -4,7 +4,8 @@ file(GLOB files unit-*.cpp) -set(GUCC_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/") +set(GUCC_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(GUCC_TOP_DIR "${CMAKE_SOURCE_DIR}") foreach(file ${files}) get_filename_component(file_basename ${file} NAME_WE) @@ -16,5 +17,6 @@ foreach(file ${files}) $<$:-Wno-deprecated-declarations> ) add_definitions(-DGUCC_TEST_DIR="${GUCC_TEST_DIR}") + add_definitions(-DGUCC_TOP_DIR="${GUCC_TOP_DIR}") target_link_libraries(${testcase} PRIVATE project_warnings project_options gucc::gucc) endforeach() diff --git a/gucc/tests/meson.build b/gucc/tests/meson.build index 99116c0..8b28b86 100644 --- a/gucc/tests/meson.build +++ b/gucc/tests/meson.build @@ -61,3 +61,11 @@ executable( link_with: [gucc_lib], include_directories: [include_directories('../include')], install: false) + +executable( + 'test-fetch_file', + files('unit-fetch_file.cpp'), + dependencies: deps, + link_with: [gucc_lib], + include_directories: [include_directories('../include')], + install: false) diff --git a/gucc/tests/unit-fetch_file.cpp b/gucc/tests/unit-fetch_file.cpp new file mode 100644 index 0000000..5b92953 --- /dev/null +++ b/gucc/tests/unit-fetch_file.cpp @@ -0,0 +1,45 @@ +#include "gucc/fetch_file.hpp" +#include "gucc/file_utils.hpp" + +#include + +#include +#include + +#include + +namespace fs = std::filesystem; +using namespace std::string_view_literals; + +int main() { + // existing remote url, non existent fallback url + static constexpr std::string_view LICENSE_PATH = GUCC_TOP_DIR "/LICENSE"; + { + static constexpr auto remote_url = "https://github.com/CachyOS/New-Cli-Installer/raw/master/LICENSE"sv; + + const auto& file_content = gucc::fetch::fetch_file_from_url(remote_url, "file:///ter-testunit"); + assert(file_content); + assert(!file_content->empty()); + + const auto& expected_file_content = gucc::file_utils::read_whole_file(LICENSE_PATH); + assert(file_content == expected_file_content); + } + // non existent remote url, existing fallback url + { + static constexpr auto remote_url = "https://github.com/CachyOS/New-Cli-Installer/raw/master/LCNS"sv; + + const auto& file_content = gucc::fetch::fetch_file_from_url(remote_url, fmt::format("file://{}", LICENSE_PATH)); + assert(file_content); + assert(!file_content->empty()); + + const auto& expected_file_content = gucc::file_utils::read_whole_file(LICENSE_PATH); + assert(file_content == expected_file_content); + } + // non existent remote url, non existent fallback url + { + static constexpr auto remote_url = "https://github.com/CachyOS/New-Cli-Installer/raw/master/LCNS"sv; + + const auto& file_content = gucc::fetch::fetch_file_from_url(remote_url, "file:///ter-testunit"); + assert(!file_content); + } +} diff --git a/meson.build b/meson.build index 253af61..23ab427 100644 --- a/meson.build +++ b/meson.build @@ -55,6 +55,7 @@ add_global_arguments('-DINSTALLER_VERSION="' + git_version + '"', language : 'cp # gucc test path add_global_arguments('-DGUCC_TEST_DIR="' + meson.current_source_dir() + '/gucc/tests/"', language : 'cpp') +add_global_arguments('-DGUCC_TOP_DIR="' + meson.current_source_dir() + '/"', language : 'cpp') # Common dependencies spdlog = dependency('spdlog', version : ['>=1.12.0'], fallback : ['spdlog', 'spdlog_dep'])