refactor: error handling with err objects

This commit is contained in:
2025-12-17 02:25:21 +03:00
parent f7c290110e
commit d4c563f585
17 changed files with 489 additions and 245 deletions

View File

@@ -1,10 +1,10 @@
#include "config.hpp"
#include "core/utils.hpp"
#include "core/error.hpp"
#include <core/palette/color.hpp>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#ifdef _WIN32
#include "windows.h"
@@ -19,14 +19,18 @@ config &config::instance()
return inst;
}
void config::initialize(std::unique_ptr<clrsync::core::io::file> file)
Result<void> config::initialize(std::unique_ptr<clrsync::core::io::file> file)
{
copy_default_configs();
m_file = std::move(file);
if (!m_file)
throw std::runtime_error{"Config file is missing"};
if (!m_file->parse())
throw std::runtime_error{"Could not parse config file"};
return Err<void>(error_code::config_missing, "Config file is missing");
auto parse_result = m_file->parse();
if (!parse_result)
return Err<void>(error_code::config_invalid, parse_result.error().message, parse_result.error().context);
return Ok();
}
std::filesystem::path config::get_user_config_dir()
@@ -65,19 +69,13 @@ void config::copy_file(const std::filesystem::path &src, const std::filesystem::
return;
if (!std::filesystem::exists(src))
{
std::cerr << "Warning: Source file does not exist: " << src << std::endl;
return;
}
std::ifstream in(src, std::ios::binary);
std::ofstream out(dst, std::ios::binary);
if (!in || !out)
{
std::cerr << "Warning: Failed to copy file from " << src << " to " << dst << std::endl;
return;
}
out << in.rdbuf();
}
@@ -85,10 +83,7 @@ void config::copy_file(const std::filesystem::path &src, const std::filesystem::
void config::copy_dir(const std::filesystem::path &src, const std::filesystem::path &dst)
{
if (!std::filesystem::exists(src))
{
std::cerr << "Warning: Source directory does not exist: " << src << std::endl;
return;
}
for (auto const &entry : std::filesystem::recursive_directory_iterator(src))
{
@@ -114,10 +109,7 @@ void config::copy_default_configs()
std::filesystem::create_directories(user_dir);
if (system_dir.empty())
{
std::cerr << "Warning: No system data directory found, skipping default config copy\n";
return;
}
{
auto src = system_dir / "config.toml";
@@ -175,50 +167,53 @@ const uint32_t config::font_size() const
return 14;
}
void config::set_default_theme(const std::string &theme)
Result<void> config::set_default_theme(const std::string &theme)
{
if (m_file)
{
m_file->set_value("general", "default_theme", theme);
m_file->save_file();
}
if (!m_file)
return Err<void>(error_code::config_missing, "Configuration not initialized");
m_file->set_value("general", "default_theme", theme);
return m_file->save_file();
}
void config::set_palettes_path(const std::string &path)
Result<void> config::set_palettes_path(const std::string &path)
{
if (m_file)
{
m_file->set_value("general", "palettes_path", path);
m_file->save_file();
}
if (!m_file)
return Err<void>(error_code::config_missing, "Configuration not initialized");
m_file->set_value("general", "palettes_path", path);
return m_file->save_file();
}
void config::set_font(const std::string &font)
Result<void> config::set_font(const std::string &font)
{
if (m_file)
{
m_file->set_value("general", "font", font);
m_file->save_file();
}
if (!m_file)
return Err<void>(error_code::config_missing, "Configuration not initialized");
m_file->set_value("general", "font", font);
return m_file->save_file();
}
void config::set_font_size(int font_size)
Result<void> config::set_font_size(int font_size)
{
if (m_file)
{
m_file->set_value("general", "font_size", font_size);
m_file->save_file();
}
if (!m_file)
return Err<void>(error_code::config_missing, "Configuration not initialized");
m_file->set_value("general", "font_size", font_size);
return m_file->save_file();
}
void config::update_template(const std::string &key,
Result<void> config::update_template(const std::string &key,
const clrsync::core::theme_template &theme_template)
{
if (!m_file)
return Err<void>(error_code::config_missing, "Configuration not initialized");
m_themes[key] = theme_template;
m_file->set_value("templates." + key, "input_path", theme_template.template_path());
m_file->set_value("templates." + key, "output_path", theme_template.output_path());
m_file->set_value("templates." + key, "enabled", theme_template.enabled());
m_file->set_value("templates." + key, "reload_cmd", theme_template.reload_command());
m_file->save_file();
return m_file->save_file();
}
const std::unordered_map<std::string, clrsync::core::theme_template> config::templates()
@@ -241,21 +236,21 @@ const std::unordered_map<std::string, clrsync::core::theme_template> config::tem
theme.set_enabled(false);
}
theme.set_reload_command(std::get<std::string>(current["reload_cmd"]));
theme.load_template();
(void)theme.load_template();
m_themes.insert({theme.name(), theme});
}
}
return m_themes;
}
const clrsync::core::theme_template &config::template_by_name(const std::string &name) const
Result<const clrsync::core::theme_template*> config::template_by_name(const std::string &name) const
{
auto it = m_themes.find(name);
if (it != m_themes.end())
{
return it->second;
return Ok(&it->second);
}
throw std::runtime_error("Template not found: " + name);
return Err<const clrsync::core::theme_template*>(error_code::template_not_found, "Template not found", name);
}
} // namespace clrsync::core

View File

@@ -3,6 +3,7 @@
#include <core/io/file.hpp>
#include <core/theme/theme_template.hpp>
#include <core/error.hpp>
#include <filesystem>
#include <memory>
#include <string>
@@ -14,23 +15,23 @@ class config
public:
static config &instance();
void initialize(std::unique_ptr<clrsync::core::io::file> file);
Result<void> initialize(std::unique_ptr<clrsync::core::io::file> file);
const std::string font() const;
const uint32_t font_size() const;
const std::string &palettes_path();
const std::string default_theme() const;
const std::unordered_map<std::string, clrsync::core::theme_template> templates();
const clrsync::core::theme_template &template_by_name(const std::string &name) const;
Result<const clrsync::core::theme_template*> template_by_name(const std::string &name) const;
std::filesystem::path get_user_config_dir();
void set_default_theme(const std::string &theme);
void set_palettes_path(const std::string &path);
void set_font(const std::string &font);
void set_font_size(int font_size);
Result<void> set_default_theme(const std::string &theme);
Result<void> set_palettes_path(const std::string &path);
Result<void> set_font(const std::string &font);
Result<void> set_font_size(int font_size);
void update_template(const std::string &key,
Result<void> update_template(const std::string &key,
const clrsync::core::theme_template &theme_template);
static std::filesystem::path get_data_dir();

207
src/core/error.hpp Normal file
View File

@@ -0,0 +1,207 @@
#ifndef CLRSYNC_CORE_ERROR_HPP
#define CLRSYNC_CORE_ERROR_HPP
#include <string>
#include <variant>
#include <optional>
namespace clrsync::core
{
enum class error_code
{
unknown,
file_not_found,
file_open_failed,
file_write_failed,
file_read_failed,
dir_create_failed,
parse_failed,
invalid_format,
config_missing,
config_invalid,
template_not_found,
template_load_failed,
template_apply_failed,
palette_not_found,
palette_load_failed,
init_failed,
invalid_arg,
resource_missing,
};
inline const char* error_code_string(error_code code)
{
switch (code)
{
case error_code::unknown: return "Unknown error";
case error_code::file_not_found: return "File not found";
case error_code::file_open_failed: return "Failed to open file";
case error_code::file_write_failed: return "Failed to write file";
case error_code::file_read_failed: return "Failed to read file";
case error_code::dir_create_failed: return "Failed to create directory";
case error_code::parse_failed: return "Parse failed";
case error_code::invalid_format: return "Invalid format";
case error_code::config_missing: return "Configuration missing";
case error_code::config_invalid: return "Configuration invalid";
case error_code::template_not_found: return "Template not found";
case error_code::template_load_failed: return "Failed to load template";
case error_code::template_apply_failed: return "Failed to apply template";
case error_code::palette_not_found: return "Palette not found";
case error_code::palette_load_failed: return "Failed to load palette";
case error_code::init_failed: return "Initialization failed";
case error_code::invalid_arg: return "Invalid argument";
case error_code::resource_missing: return "Resource missing";
default: return "Unknown error code";
}
}
struct Error
{
error_code code;
std::string message;
std::string context;
Error(error_code c) : code(c), message(error_code_string(c)) {}
Error(error_code c, std::string msg)
: code(c), message(std::move(msg)) {}
Error(error_code c, std::string msg, std::string ctx)
: code(c), message(std::move(msg)), context(std::move(ctx)) {}
std::string description() const
{
if (context.empty())
return message;
return message + " [" + context + "]";
}
};
template<typename T>
class [[nodiscard]] Result
{
private:
std::variant<T, Error> m_data;
public:
Result(T value) : m_data(std::move(value)) {}
Result(Error error) : m_data(std::move(error)) {}
bool is_ok() const { return std::holds_alternative<T>(m_data); }
bool is_error() const { return std::holds_alternative<Error>(m_data); }
explicit operator bool() const { return is_ok(); }
T& value() & { return std::get<T>(m_data); }
const T& value() const & { return std::get<T>(m_data); }
T&& value() && { return std::get<T>(std::move(m_data)); }
const Error& error() const { return std::get<Error>(m_data); }
T value_or(T default_value) const
{
return is_ok() ? std::get<T>(m_data) : std::move(default_value);
}
std::optional<T> ok() const
{
if (is_ok())
return std::get<T>(m_data);
return std::nullopt;
}
std::optional<Error> err() const
{
if (is_error())
return std::get<Error>(m_data);
return std::nullopt;
}
template<typename F>
auto map(F&& func) -> Result<decltype(func(std::declval<T>()))>
{
using U = decltype(func(std::declval<T>()));
if (is_ok())
return Result<U>(func(std::get<T>(m_data)));
return Result<U>(std::get<Error>(m_data));
}
template<typename F>
auto and_then(F&& func) -> decltype(func(std::declval<T>()))
{
if (is_ok())
return func(std::get<T>(m_data));
using ResultType = decltype(func(std::declval<T>()));
return ResultType(std::get<Error>(m_data));
}
};
template<>
class [[nodiscard]] Result<void>
{
private:
std::optional<Error> m_error;
public:
Result() : m_error(std::nullopt) {}
Result(Error error) : m_error(std::move(error)) {}
bool is_ok() const { return !m_error.has_value(); }
bool is_error() const { return m_error.has_value(); }
explicit operator bool() const { return is_ok(); }
const Error& error() const { return *m_error; }
std::optional<Error> err() const { return m_error; }
};
template<typename T>
Result<T> Ok(T value)
{
return Result<T>(std::move(value));
}
inline Result<void> Ok()
{
return Result<void>();
}
template<typename T>
Result<T> Err(Error error)
{
return Result<T>(std::move(error));
}
template<typename T>
Result<T> Err(error_code code)
{
return Result<T>(Error(code));
}
template<typename T>
Result<T> Err(error_code code, std::string message)
{
return Result<T>(Error(code, std::move(message)));
}
template<typename T>
Result<T> Err(error_code code, std::string message, std::string context)
{
return Result<T>(Error(code, std::move(message), std::move(context)));
}
} // namespace clrsync::core
#endif // CLRSYNC_CORE_ERROR_HPP

View File

@@ -4,6 +4,7 @@
#include <map>
#include <string>
#include <variant>
#include <core/error.hpp>
using value_type = std::variant<std::string, uint32_t, int, bool>;
@@ -14,7 +15,8 @@ class file
public:
file() = default;
file(std::string path) {};
virtual bool parse() { return false; };
virtual ~file() = default;
virtual Result<void> parse() { return Ok(); };
virtual const std::string get_string_value(const std::string &section,
const std::string &key) const
{
@@ -39,7 +41,7 @@ class file
}
virtual void insert_or_update_value(const std::string &section, const std::string &key,
const value_type &value) {};
virtual void save_file() {};
virtual Result<void> save_file() { return Ok(); };
};
} // namespace clrsync::core::io
#endif

View File

@@ -11,12 +11,13 @@ toml_file::toml_file(std::string path)
m_path = expand_user(path);
}
bool toml_file::parse()
Result<void> toml_file::parse()
{
if (!std::filesystem::exists(m_path))
return false;
return Err<void>(error_code::file_not_found, "File does not exist", m_path);
m_file = toml::parse_file(m_path);
return true;
return Ok();
}
const std::string toml_file::get_string_value(const std::string &section,
@@ -91,11 +92,23 @@ void toml_file::insert_or_update_value(const std::string &section, const std::st
std::visit([&](auto &&v) { tbl->insert_or_assign(key, v); }, value);
}
void toml_file::save_file()
Result<void> toml_file::save_file()
{
std::filesystem::create_directories(std::filesystem::path(m_path).parent_path());
try {
std::filesystem::create_directories(std::filesystem::path(m_path).parent_path());
} catch (const std::exception& e) {
return Err<void>(error_code::dir_create_failed, e.what(), m_path);
}
std::ofstream stream(m_path, std::ios::binary);
if (!stream)
return Err<void>(error_code::file_write_failed, "Failed to open file for writing", m_path);
stream << m_file;
if (!stream)
return Err<void>(error_code::file_write_failed, "Failed to write to file", m_path);
return Ok();
}
std::vector<std::string> toml_file::split(const std::string &s, char delim) const

View File

@@ -1,6 +1,7 @@
#ifndef CLRSYNC_CORE_IO_TOML_FILE_HPP
#define CLRSYNC_CORE_IO_TOML_FILE_HPP
#include <core/io/file.hpp>
#include <core/error.hpp>
#include <string>
#include <toml/toml.hpp>
@@ -10,7 +11,7 @@ class toml_file : public file
{
public:
explicit toml_file(std::string path);
bool parse() override;
Result<void> parse() override;
const std::string get_string_value(const std::string &section,
const std::string &key) const override;
uint32_t get_uint_value(const std::string &section, const std::string &key) const override;
@@ -18,7 +19,7 @@ class toml_file : public file
std::map<std::string, value_type> get_table(const std::string &section_path) const override;
void insert_or_update_value(const std::string &section, const std::string &key,
const value_type &value) override;
void save_file() override;
Result<void> save_file() override;
private:
toml::parse_result m_file{};

View File

@@ -52,7 +52,7 @@ template <typename FileType> class palette_file
}
void save()
{
m_file->save_file();
(void)m_file->save_file();
}
private:

View File

@@ -21,10 +21,7 @@ template <typename FileType> class palette_manager
{
auto directory_path_expanded = expand_user(directory_path);
if (!std::filesystem::exists(directory_path_expanded))
{
std::cerr << "Palettes directory does not exist\n" ;
return;
}
for (const auto &entry : std::filesystem::directory_iterator(directory_path_expanded))
{
if (entry.is_regular_file())

View File

@@ -3,6 +3,7 @@
#include <core/config/config.hpp>
#include <core/palette/palette_manager.hpp>
#include <core/theme/template_manager.hpp>
#include <core/error.hpp>
#include <string>
namespace clrsync::core
@@ -18,38 +19,52 @@ template <typename FileType> class theme_renderer
m_template_manager = template_manager<FileType>();
}
void apply_theme(const std::string &theme_name)
Result<void> apply_theme(const std::string &theme_name)
{
auto palette = m_pal_manager.get_palette(theme_name);
if (!palette)
throw std::runtime_error("Palette not found: " + theme_name);
apply_palette_to_all_templates(*palette);
return Err<void>(error_code::palette_not_found, "Palette not found", theme_name);
return apply_palette_to_all_templates(*palette);
}
void apply_theme_from_path(const std::string &path)
Result<void> apply_theme_from_path(const std::string &path)
{
auto palette = m_pal_manager.load_palette_from_file(path);
apply_palette_to_all_templates(palette);
return apply_palette_to_all_templates(palette);
}
private:
palette_manager<FileType> m_pal_manager;
template_manager<FileType> m_template_manager;
void apply_palette_to_all_templates(const palette &pal)
Result<void> apply_palette_to_all_templates(const palette &pal)
{
for (auto &t_pair : m_template_manager.templates())
{
auto &tmpl = t_pair.second;
if (!tmpl.enabled())
continue;
tmpl.load_template();
auto load_result = tmpl.load_template();
if (!load_result)
return load_result;
tmpl.apply_palette(pal);
tmpl.save_output();
auto save_result = tmpl.save_output();
if (!save_result)
return save_result;
if (!tmpl.reload_command().empty())
{
std::system(tmpl.reload_command().c_str());
int result = std::system(tmpl.reload_command().c_str());
if (result != 0)
{
std::cerr << "Warning: Command " << tmpl.reload_command() << " failed with code " << result << "\n";
}
}
}
return Ok();
}
};

View File

@@ -43,21 +43,21 @@ void theme_template::set_output_path(const std::string &path)
m_output_path = expand_user(path);
}
void theme_template::load_template()
Result<void> theme_template::load_template()
{
if (!std::filesystem::exists(m_template_path))
{
std::cerr << "Warning: Template file '" << m_template_path << "' is missing\n";
return;
return Err<void>(error_code::template_not_found, "Template file is missing", m_template_path);
}
std::ifstream input(m_template_path, std::ios::binary);
if (!input)
{
std::cerr << "Warning: Failed to open template file: " << m_template_path << std::endl;
return;
return Err<void>(error_code::template_load_failed, "Failed to open template file", m_template_path);
}
m_template_data.assign(std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>());
return Ok();
}
void theme_template::apply_palette(const core::palette &palette)
@@ -90,7 +90,7 @@ void theme_template::apply_palette(const core::palette &palette)
}
}
void theme_template::save_output() const
Result<void> theme_template::save_output() const
{
try
{
@@ -98,18 +98,22 @@ void theme_template::save_output() const
}
catch (const std::exception& e)
{
std::cerr << "Warning: Failed to create output directory for " << m_output_path << ": " << e.what() << std::endl;
return;
return Err<void>(error_code::dir_create_failed, e.what(), m_output_path);
}
std::ofstream output(m_output_path, std::ios::binary);
if (!output)
{
std::cerr << "Warning: Failed to write output file: " << m_output_path << std::endl;
return;
return Err<void>(error_code::file_write_failed, "Failed to open output file for writing", m_output_path);
}
output << m_processed_data;
if (!output)
{
return Err<void>(error_code::file_write_failed, "Failed to write to output file", m_output_path);
}
return Ok();
}
const std::string &theme_template::raw_template() const

View File

@@ -2,6 +2,7 @@
#define clrsync_CORE_IO_THEME_TEMPLATE_HPP
#include <core/palette/palette.hpp>
#include <core/error.hpp>
#include <string>
namespace clrsync::core
@@ -26,11 +27,11 @@ class theme_template
void set_output_path(const std::string &path);
void load_template();
Result<void> load_template();
void apply_palette(const core::palette &palette);
void save_output() const;
Result<void> save_output() const;
const std::string &raw_template() const;