diff --git a/CMakeLists.txt b/CMakeLists.txt index cebe54d..f914b59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") endif() find_package(nlohmann_json REQUIRED) +find_package(CURL REQUIRED) add_executable(${PROJECT_NAME}) @@ -42,6 +43,7 @@ add_subdirectory(src) add_subdirectory(test) target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl) install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 73d73e6..0a04c7f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,12 @@ set(SOURCES main.cpp + loggger.cpp project.cpp dependency.cpp ) set(HEADERS + logger.h project.h dependency.cpp ) diff --git a/src/dependency.cpp b/src/dependency.cpp index c1aec3c..9f83724 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -1,5 +1,11 @@ #include "dependency.h" +#include + +#include "project.h" + +const std::regex http("^https?://"); + Dependency::Dependency( const std::string& path, Type type, @@ -23,3 +29,69 @@ std::optional Dependency::getName() const { std::optional Dependency::getVersion() const { return version; } + +bool Dependency::prepare(const std::filesystem::path& source, const std::filesystem::path& destination) { + if (std::regex_search(path, http)) { + Project::info("downloading project from " + path); + std::filesystem::create_directories(destination / "downloads"); + + return download(destination); + } else { + Project::info("checking project at path " + path); + std::filesystem::directory_entry srcDir(source / path); + if (!srcDir.exists()) { + Project::error("Project at " + path + " doesn't exist"); + return false; + } + } + + return true; +} + +bool Dependency::download(const std::filesystem::path& destination) { + std::filesystem::path dwn = destination / "downloads"; + bool result = std::filesystem::create_directories(dwn); + // if (!result) { + // Project::error("couldn't create " + dwn.string()); + // return result; + // } + + CURL *curl_handle; + std::filesystem::path out = dwn / ("out" + std::to_string(std::rand())); + FILE *pagefile; + + curl_global_init(CURL_GLOBAL_ALL); + /* init the curl session */ + curl_handle = curl_easy_init(); + /* set URL to get here */ + curl_easy_setopt(curl_handle, CURLOPT_URL, path.c_str()); + /* disable progress meter, set to 0L to enable it */ + curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write); + + /* open the file */ + pagefile = fopen(out.c_str(), "wb"); + if (pagefile) { + /* write the page body to this file handle */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile); + /* get it! */ + curl_easy_perform(curl_handle); + /* close the header file */ + fclose(pagefile); + } else { + Project::error(std::string("couldn't open output file ") + out.c_str()); + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + curl_global_cleanup(); + + return true; +} + + +size_t Dependency::write(void* file, size_t size, size_t nmemb, void* stream) { + size_t written = fwrite(file, size, nmemb, (FILE *)stream); + return written; +} diff --git a/src/dependency.h b/src/dependency.h index 0cea750..fa203e1 100644 --- a/src/dependency.h +++ b/src/dependency.h @@ -2,6 +2,10 @@ #include #include +#include + +#include +#include class Dependency { public: @@ -22,8 +26,14 @@ public: std::optional getName() const; std::optional getVersion() const; + bool prepare(const std::filesystem::path& source, const std::filesystem::path& destination); + const std::string path; +private: + static size_t write(void *file, size_t size, size_t nmemb, void *stream); + bool download(const std::filesystem::path& destination); + private: Type type; std::optional name; diff --git a/src/loggger.cpp b/src/loggger.cpp new file mode 100644 index 0000000..60f6ce8 --- /dev/null +++ b/src/loggger.cpp @@ -0,0 +1,59 @@ +#include "loggger.h" + +constexpr const std::array(Logger::Severity::fatal) + 1> logSettings({ + /*debug*/ "\e[90m", + /*info*/ "\e[32m", + /*minor*/ "\e[34m", + /*major*/ "\e[94m", + /*warning*/ "\e[33m", + /*error*/ "\e[31m", + /*fatal*/ "\e[91m" +}); + +constexpr const std::array(Logger::Severity::fatal) + 1> logHeaders({ + /*debug*/ "DEBUG: ", + /*info*/ "INFO: ", + /*minor*/ "MINOR: ", + /*major*/ "MAJOR: ", + /*warning*/ "WARNING: ", + /*error*/ "ERROR: ", + /*fatal*/ "FATAL: " +}); + +constexpr const std::string_view bold("\e[1m"); +constexpr const std::string_view regular("\e[22m"); +constexpr const std::string_view clearStyle("\e[0m"); + +Logger::Logger(Logger::Severity severity): + currentSeverity(severity), + history(), + readMutex(), + writeMutex() +{} + +Logger::~Logger() +{} + +void Logger::printLog(bool colored) { + std::cout << std::endl; + for (const Message& message : history) { + if (colored) { + int severity = static_cast(message.first); + std::cout << logSettings[severity] << bold << logHeaders[severity] << regular; + } + std::cout << message.second << std::endl; + } + if (colored) + std::cout << clearStyle << std::flush; +} + +void Logger::log(Logger::Severity severity, const std::string& comment) const { + std::scoped_lock lock(readMutex, writeMutex); + if (severity >= currentSeverity) + history.emplace_back(severity, comment); +} + +std::list> Logger::getLog() const { + std::scoped_lock lock(readMutex); + return history; +} diff --git a/src/loggger.h b/src/loggger.h new file mode 100644 index 0000000..5dbb0b4 --- /dev/null +++ b/src/loggger.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Logger { +public: + enum class Severity { + debug, + info, + minor, + major, + warning, + error, + fatal + }; + typedef std::pair Message; + + Logger(Severity severity = Severity::info); + ~Logger(); + + void log (Severity severity, const std::string& comment) const; + std::list getLog() const; + void printLog(bool colored = true); + +private: + const Severity currentSeverity; + mutable std::list history; + mutable std::mutex readMutex; + mutable std::mutex writeMutex; + +}; diff --git a/src/main.cpp b/src/main.cpp index f3d76b1..b478be1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,29 +1,34 @@ -#include #include #include "project.h" int main(int argc, char *argv[]) { std::string firstArg; + std::string secondArg; if (argc > 1) firstArg = argv[1]; else firstArg = "./"; - Project root(firstArg); + if (argc > 2) + secondArg = argv[2]; + else + secondArg = "./"; + + Project root(firstArg, secondArg); bool success = root.read(); - if (!success) { - Project::Log log(root.getLog()); - for (const Project::LogMessage& msg : log) - std::cout << msg << std::endl; + int result = -1; + if (success) { + root.info("successfully parsed project " + root.getName()); + root.info("dependencies count is " + std::to_string(root.dependenciesCount())); - return -1; - } else { - std::cout << "successfully parsed project " << root.getName() << std::endl; - std::cout << "dependencies count is " << root.dependenciesCount() << std::endl; + root.discover(); + result = 0; } + root.printLog(); - return 0; + + return result; } diff --git a/src/project.cpp b/src/project.cpp index 45531ef..114e5fe 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -15,31 +15,54 @@ std::map types({ {"automatic", Dependency::Type::automatic} }); -Project::Project(const std::filesystem::path& location): +Logger* Project::logger = nullptr; + +Project::Project(const std::filesystem::path& location, const std::filesystem::path& destination): location(location), - status(Status::unknown), + destination(destination), + state(State::unknown), name(), - logStorage(), - dependencies() -{} + dependencies(), + root(logger == nullptr) +{ + if (root) + logger = new Logger(); +} + +Project::~Project() { + if (root) { + delete logger; + logger = nullptr; + } +} bool Project::read() { - if (status == Status::read) + if (state == State::read) return true; std::filesystem::path entryPointPath; try { - entryPointPath = std::filesystem::canonical(location) / entry; + location = std::filesystem::canonical(location); + entryPointPath = location / entry; } catch (const std::exception& e) { - log(e.what()); - status = Status::error; + fatal(e.what()); + state = State::error; + return false; + } + + try { + std::filesystem::create_directories(destination); + destination = std::filesystem::canonical(destination); + } catch (const std::exception& e) { + fatal(e.what()); + state = State::error; return false; } std::ifstream file(entryPointPath); if (!file.is_open()) { - log("couldn't open " + std::string(entryPointPath)); - status = Status::error; + fatal("couldn't open " + entryPointPath.string()); + state = State::error; return false; } @@ -47,16 +70,16 @@ bool Project::read() { try { data = json::parse(file); } catch (const json::exception& e) { - log(e.what()); - status = Status::error; + fatal(e.what()); + state = State::error; return false; } try { parse(data); } catch (const std::exception& e) { - log(e.what()); - status = Status::error; + fatal(e.what()); + state = State::error; return false; } @@ -126,21 +149,54 @@ void Project::createDepencencyFromObject(const nlohmann::json& entry) { dependencies.emplace(url, Dependency{url, type, name, version}); } +void Project::discover() { + int fine = 0; + for (std::pair& pair : dependencies) { + bool success = pair.second.prepare(location, destination); + if (success) + fine++; + } + + +} + + uint32_t Project::dependenciesCount() const { return dependencies.size(); } - -void Project::log(const std::string& message) const { - logStorage.emplace_back(message); +void Project::log(Logger::Severity severity, const std::string& message) { + if (logger != nullptr) + logger->log(severity, message); } -Project::Log Project::getLog() const { - return logStorage; +void Project::info(const std::string& message) { + log(Logger::Severity::info, message); } -Project::Status Project::getStatus() const { - return status; +void Project::debug(const std::string& message) { + log(Logger::Severity::debug, message); +} + +void Project::error(const std::string& message) { + log(Logger::Severity::error, message); +} + +void Project::warn(const std::string& message) { + log(Logger::Severity::warning, message); +} + +void Project::fatal(const std::string& message) { + log(Logger::Severity::fatal, message); +} + +void Project::printLog() { + if (logger != nullptr) + logger->printLog(); +} + +Project::State Project::getStatus() const { + return state; } std::string Project::getName() const { diff --git a/src/project.h b/src/project.h index 1ea6195..f467cdd 100644 --- a/src/project.h +++ b/src/project.h @@ -10,35 +10,47 @@ #include #include "dependency.h" +#include "loggger.h" class Project { public: - typedef std::string LogMessage; - typedef std::list Log; - enum class Status { + enum class State { unknown, read, + discovering, error }; - Project(const std::filesystem::path& location); + Project(const std::filesystem::path& location, const std::filesystem::path& destination); + ~Project(); + bool read(); - Status getStatus() const; - Log getLog() const; + void discover(); + State getStatus() const; std::string getName() const; uint32_t dependenciesCount() const; + static void log(Logger::Severity severity, const std::string& message); + static void debug(const std::string& message); + static void info(const std::string& message); + static void warn(const std::string& message); + static void error(const std::string& message); + static void fatal(const std::string& message); + static void printLog(); + private: - void log(const std::string& message) const; void parse(const nlohmann::json& json); void createDepencencyFromString(const std::string& entry); void createDepencencyFromObject(const nlohmann::json& entry); private: std::filesystem::path location; - Status status; + std::filesystem::path destination; + State state; std::string name; - mutable Log logStorage; std::map dependencies; + const bool root; + + static Logger* logger; };