diff --git a/build.ninja b/build.ninja new file mode 100644 index 0000000..0d2507b --- /dev/null +++ b/build.ninja @@ -0,0 +1,16 @@ +rule cxx + command = g++ -c -o $out $in + description = CXX $out + +rule link + command = g++ -o $out $in + description = LINK $out + +build build/package.o: cxx src/package.cpp | src/package.h +build build/command.o: cxx src/command.cpp | src/command.h build/package.o + +build build/main.o: cxx src/main.cpp + +build build/slackbuilder: link build/main.o build/command.o build/package.o + +default build/slackbuilder diff --git a/src/command.cpp b/src/command.cpp new file mode 100644 index 0000000..4066c23 --- /dev/null +++ b/src/command.cpp @@ -0,0 +1,74 @@ +#include "command.h" +#include + +namespace katja +{ + void list::execute() const + { + for (const auto& package : katja::read_package_database()) + { + std::cout << package.second.name() + << " " << package.second.version() + << " (" << package.second.tag() << ")" + << std::endl; + } + } + + void help::execute() const + { + std::cout << "Usage:\n" + "\tkatja {info|help} [OPTIONS]\n\n"; + } + + command_exception::command_exception(const command_exception_t exception_type, + std::vector failed_arguments) noexcept + : m_exception_type(exception_type), m_failed_arguments(failed_arguments) + { + } + + const char *command_exception::what() const noexcept + { + switch (m_exception_type) + { + case command_exception_t::no_command: + return "No command specified."; + case command_exception_t::too_many_arguments: + return "Too many arguments given."; + case command_exception_t::unknown_command: + return "Unknown command."; + } + assert(false); + } + + const std::vector& command_exception::failed_arguments() const noexcept + { + return m_failed_arguments; + } + + std::unique_ptr parse_command_line(int argc, char **argv) + { + if (argc > 2) + { + std::vector failed_arguments; + + for (std::size_t i = 2; i < argc; ++i) + { + failed_arguments.push_back(argv[i]); + } + throw command_exception(command_exception_t::unknown_command); + } + else if (argc < 2) + { + throw command_exception(command_exception_t::no_command); + } + if (strcmp(argv[1], "list") == 0) + { + return std::make_unique(); + } + else if (strcmp(argv[1], "help") == 0) + { + return std::make_unique(); + } + throw command_exception(command_exception_t::unknown_command, { argv[1] }); + } +} diff --git a/src/command.h b/src/command.h new file mode 100644 index 0000000..8d0679d --- /dev/null +++ b/src/command.h @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include "package.h" + +namespace katja +{ + class command + { + public: + virtual void execute() const = 0; + }; + + class list final : public command + { + public: + void execute() const override; + }; + + class help final : public command + { + public: + void execute() const override; + }; + + enum class command_exception_t + { + no_command, + too_many_arguments, + unknown_command, + }; + + class command_exception final : public std::exception + { + command_exception_t m_exception_type; + std::vector m_failed_arguments; + + public: + explicit command_exception(const command_exception_t exception_type, + std::vector failed_arguments = {}) noexcept; + + const char *what() const noexcept override; + const std::vector& failed_arguments() const noexcept; + }; + + std::unique_ptr parse_command_line(int argc, char **argv); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ef8dfcd --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,22 @@ +#include "command.h" + +int main(int argc, char **argv) +{ + std::unique_ptr command; + + try + { + command = katja::parse_command_line(argc, argv); + } + catch (katja::command_exception& e) + { + std::cout << e.what() << std::endl << std::endl; + + katja::help().execute(); + + return EXIT_FAILURE; + } + command->execute(); + + return EXIT_SUCCESS; +} diff --git a/src/package.cpp b/src/package.cpp new file mode 100644 index 0000000..0734a5e --- /dev/null +++ b/src/package.cpp @@ -0,0 +1,76 @@ +#include "package.h" +#include + +namespace katja +{ + package::package(const std::string& name, const std::string& version, + const std::string& architecture, const std::string& tag) + : m_name(name), m_version(version), m_architecture(architecture), m_tag(tag) + { + } + + const std::string& package::name() const noexcept + { + return m_name; + } + + const std::string& package::version() const noexcept + { + return m_version; + } + + const std::string& package::architecture() const noexcept + { + return m_architecture; + } + + const std::string& package::tag() const noexcept + { + return m_tag; + } + + std::optional package::parse(const std::string& full_name) noexcept + { + std::size_t tag_separator = full_name.find_last_of('-'); + + if (tag_separator == std::string::npos || tag_separator == 0) + { + return std::nullopt; + } + std::size_t architecture_separator = full_name.find_last_of('-', tag_separator - 1); + + if (architecture_separator == std::string::npos || architecture_separator == 0) + { + return std::nullopt; + } + std::size_t version_separator = full_name.find_last_of('-', architecture_separator - 1); + + if (version_separator == std::string::npos || version_separator == 0) + { + return std::nullopt; + } + package parsed_package{ + full_name.substr(0, version_separator), + full_name.substr(version_separator + 1, architecture_separator - version_separator - 1), + full_name.substr(architecture_separator + 1, tag_separator - architecture_separator - 1), + full_name.substr(tag_separator + 1) + }; + return std::make_optional(parsed_package); + } + + std::unordered_map read_package_database() + { + std::unordered_map packages; + + for (const auto& entry : std::filesystem::directory_iterator("/var/lib/pkgtools/packages")) + { + std::optional maybe_package = package::parse(entry.path().filename()); + + if (maybe_package.has_value()) + { + packages.insert_or_assign(maybe_package.value().name(), maybe_package.value()); + } + } + return packages; + } +} diff --git a/src/package.h b/src/package.h new file mode 100644 index 0000000..5ffd060 --- /dev/null +++ b/src/package.h @@ -0,0 +1,27 @@ +#include +#include +#include + +namespace katja +{ + class package + { + std::string m_name; + std::string m_version; + std::string m_architecture; + std::string m_tag; + + public: + explicit package(const std::string& name, const std::string& version, + const std::string& architecture, const std::string& tag); + + const std::string& name() const noexcept; + const std::string& version() const noexcept; + const std::string& architecture() const noexcept; + const std::string& tag() const noexcept; + + static std::optional parse(const std::string& full_name) noexcept; + }; + + std::unordered_map read_package_database(); +} diff --git a/test.sh b/test.sh deleted file mode 100755 index c436583..0000000 --- a/test.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -bundle install -bundle exec rubocop