Add an utility to list all installed packages

This commit is contained in:
Eugen Wissner 2023-04-01 14:16:44 +02:00
parent 0385dbbe53
commit f46a16b4a0
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
7 changed files with 263 additions and 4 deletions

16
build.ninja Normal file
View File

@ -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

74
src/command.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "command.h"
#include <cassert>
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<std::string> 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<std::string>& command_exception::failed_arguments() const noexcept
{
return m_failed_arguments;
}
std::unique_ptr<command> parse_command_line(int argc, char **argv)
{
if (argc > 2)
{
std::vector<std::string> 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<list>();
}
else if (strcmp(argv[1], "help") == 0)
{
return std::make_unique<help>();
}
throw command_exception(command_exception_t::unknown_command, { argv[1] });
}
}

48
src/command.h Normal file
View File

@ -0,0 +1,48 @@
#include <iostream>
#include <cstring>
#include <memory>
#include <vector>
#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<std::string> m_failed_arguments;
public:
explicit command_exception(const command_exception_t exception_type,
std::vector<std::string> failed_arguments = {}) noexcept;
const char *what() const noexcept override;
const std::vector<std::string>& failed_arguments() const noexcept;
};
std::unique_ptr<command> parse_command_line(int argc, char **argv);
}

22
src/main.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "command.h"
int main(int argc, char **argv)
{
std::unique_ptr<katja::command> 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;
}

76
src/package.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "package.h"
#include <filesystem>
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> 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<std::string, package> read_package_database()
{
std::unordered_map<std::string, package> packages;
for (const auto& entry : std::filesystem::directory_iterator("/var/lib/pkgtools/packages"))
{
std::optional<package> 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;
}
}

27
src/package.h Normal file
View File

@ -0,0 +1,27 @@
#include <string>
#include <optional>
#include <unordered_map>
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<package> parse(const std::string& full_name) noexcept;
};
std::unordered_map<std::string, package> read_package_database();
}

View File

@ -1,4 +0,0 @@
#!/bin/sh
bundle install
bundle exec rubocop