refactor(pipeline): restructure config, add PromptDirectory, consolidate SQLite layer (#217)

* Refactor ApplicationOptions to separate config concerns

* add prompt dir app option

* readability updates: remove magic numbers, update comments

* codebase formatting

* Update docs

* Extract argument parsing, timer out of
This commit is contained in:
2026-05-02 18:27:14 -04:00
committed by GitHub
parent 641a479b6a
commit b1dc8e0b5d
35 changed files with 561 additions and 310 deletions

View File

@@ -6,6 +6,8 @@
* @brief Abstraction for persisting generated brewery data.
*/
#include <cstdint>
#include "data_model/generated_brewery.h"
/**

View File

@@ -0,0 +1,76 @@
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_PROMPT_DIRECTORY_H_
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_PROMPT_DIRECTORY_H_
/**
* @file services/prompt_directory.h
* @brief Interface and filesystem-backed implementation for named prompt
* loading.
*
* Prompt files are resolved by key: a key of "BREWERY_GENERATION" maps to the
* file <prompt_dir>/BREWERY_GENERATION.md. The interface is kept intentionally
* narrow so test doubles can be injected without touching the filesystem.
*/
#include <filesystem>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
/**
* @brief Interface for loading named prompt files.
*/
class IPromptDirectory {
public:
IPromptDirectory() = default;
IPromptDirectory(const IPromptDirectory&) = delete;
IPromptDirectory& operator=(const IPromptDirectory&) = delete;
IPromptDirectory(IPromptDirectory&&) = delete;
IPromptDirectory& operator=(IPromptDirectory&&) = delete;
virtual ~IPromptDirectory() = default;
/**
* @brief Loads the prompt associated with @p key.
*
* @param key Logical prompt key, e.g. "BREWERY_GENERATION".
* @return Prompt text.
* @throws std::runtime_error if the prompt file cannot be found or read.
*/
[[nodiscard]] virtual std::string Load(std::string_view key) = 0;
};
/**
* @brief Filesystem-backed IPromptDirectory implementation.
*
* Each call to Load() checks an in-process cache first, then reads
* <prompt_dir>/<key>.md from disk. The directory must exist and be readable
* at construction time; individual file absence is reported lazily at Load().
*/
class PromptDirectory final : public IPromptDirectory {
public:
/**
* @brief Constructs a PromptDirectory rooted at @p prompt_dir.
*
* @param prompt_dir Absolute or relative path to the prompt directory.
* @throws std::runtime_error if @p prompt_dir does not exist or is not a
* directory.
*/
explicit PromptDirectory(const std::filesystem::path& prompt_dir);
/**
* @brief Loads the prompt for @p key, caching the result.
*
* Maps @p key → <prompt_dir>/<key>.md.
*
* @param key Logical prompt key.
* @return Prompt text.
* @throws std::runtime_error if the file does not exist or is empty.
*/
[[nodiscard]] std::string Load(std::string_view key) override;
private:
std::filesystem::path prompt_dir_;
std::unordered_map<std::string, std::string> cache_;
};
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_PROMPT_DIRECTORY_H_

View File

@@ -7,6 +7,7 @@
*/
#include <sqlite3.h>
#include <filesystem>
#include <string>
#include <string_view>
@@ -20,12 +21,10 @@ void ThrowSqliteError(sqlite3* db_handle, std::string_view action);
SqliteDatabaseHandle OpenDatabase(const std::filesystem::path& path);
void ExecSql(const SqliteDatabaseHandle& db_handle, std::string_view sql,
const char* action);
const char* action);
void RollbackTransactionNoThrow(const SqliteDatabaseHandle& db_handle) noexcept;
} // namespace sqlite_export_service_internal
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_SQLITE_CONNECTION_HELPERS_H_

View File

@@ -11,6 +11,7 @@
#include <string>
#include <unordered_map>
#include "data_model/application_options.h"
#include "services/date_time_provider.h"
#include "services/export_service.h"
#include "services/sqlite_export_service_helpers.h"
@@ -20,7 +21,7 @@
*/
class SqliteExportService final : public IExportService {
public:
SqliteExportService();
explicit SqliteExportService(const ApplicationOptions& options);
~SqliteExportService() override;
SqliteExportService(const SqliteExportService&) = delete;
@@ -41,12 +42,12 @@ class SqliteExportService final : public IExportService {
void InitializeSchema() const;
void PrepareStatements();
void RollbackAndCloseNoThrow() noexcept;
void FinalizeStatements() noexcept;
[[nodiscard]] std::filesystem::path BuildDatabasePath() const;
[[nodiscard]] static std::string BuildLocationKey(const Location& location);
std::unique_ptr<IDateTimeProvider> date_time_provider_;
std::filesystem::path output_path_;
std::string run_timestamp_utc_;
std::filesystem::path database_path_;
SqliteDatabaseHandle db_handle_;

View File

@@ -3,8 +3,8 @@
/* Umbrella header for backward compatibility. */
#include "services/sqlite_handle_types.h"
#include "services/sqlite_connection_helpers.h"
#include "services/sqlite_handle_types.h"
#include "services/sqlite_statement_helpers.h"
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_SQLITE_EXPORT_SERVICE_HELPERS_H_

View File

@@ -6,6 +6,7 @@
*/
#include <sqlite3.h>
#include <memory>
#include <string_view>
@@ -33,4 +34,3 @@ struct BindParam {
} // namespace sqlite_export_service_internal
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_SQLITE_HANDLE_TYPES_H_

View File

@@ -3,10 +3,12 @@
/**
* @file services/sqlite_statement_helpers.h
* @brief Declarations for statement-level SQLite helper functions and constants.
* @brief Declarations for statement-level SQLite helper functions and
* constants.
*/
#include <sqlite3.h>
#include <string>
#include <string_view>
#include <vector>
@@ -107,10 +109,8 @@ void StepStatement(const SqliteDatabaseHandle& db_handle,
sqlite3_int64 LastInsertRowId(const SqliteDatabaseHandle& db_handle);
std::string SerializeLocalLanguages(const std::vector<std::string>& local_languages);
std::string SerializeVector(const std::vector<std::string>& str_vec);
} // namespace sqlite_export_service_internal
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_SQLITE_STATEMENT_HELPERS_H_

View File

@@ -0,0 +1,35 @@
#ifndef BIERGARTEN_PIPELINE_INCLUDES_SERVICES_TIMER_H_
#define BIERGARTEN_PIPELINE_INCLUDES_SERVICES_TIMER_H_
#include <chrono>
/**
* @file services/timer.h
* @brief Simple timer utility for measuring elapsed time.
*/
class Timer {
std::chrono::steady_clock::time_point start_time =
std::chrono::steady_clock::now();
public:
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
Timer(Timer&&) = delete;
Timer& operator=(Timer&&) = delete;
Timer() = default;
~Timer() = default;
[[nodiscard]] int64_t Elapsed() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_time)
.count();
}
[[nodiscard]] int64_t Reset() {
auto previous_elapsed = Elapsed();
start_time = std::chrono::steady_clock::now();
return previous_elapsed;
}
};
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_TIMER_H_