mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Formatting
This commit is contained in:
@@ -25,26 +25,26 @@ using SurnamesByCountryMap = std::unordered_map<std::string, SurnameList>;
|
||||
* brewery/user generation.
|
||||
*/
|
||||
class ICuratedDataService {
|
||||
public:
|
||||
ICuratedDataService() = default;
|
||||
virtual ~ICuratedDataService() = default;
|
||||
public:
|
||||
ICuratedDataService() = default;
|
||||
virtual ~ICuratedDataService() = default;
|
||||
|
||||
ICuratedDataService(const ICuratedDataService&) = delete;
|
||||
ICuratedDataService& operator=(const ICuratedDataService&) = delete;
|
||||
ICuratedDataService(ICuratedDataService&&) = delete;
|
||||
ICuratedDataService& operator=(ICuratedDataService&&) = delete;
|
||||
ICuratedDataService(const ICuratedDataService&) = delete;
|
||||
ICuratedDataService& operator=(const ICuratedDataService&) = delete;
|
||||
ICuratedDataService(ICuratedDataService&&) = delete;
|
||||
ICuratedDataService& operator=(ICuratedDataService&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Loads all curated location records.
|
||||
*/
|
||||
virtual const LocationsList& LoadLocations() = 0;
|
||||
/**
|
||||
* @brief Loads all curated location records.
|
||||
*/
|
||||
virtual const LocationsList& LoadLocations() = 0;
|
||||
|
||||
/**
|
||||
* @brief Loads all curated persona records.
|
||||
*/
|
||||
virtual const PersonasList& LoadPersonas() = 0;
|
||||
virtual const ForenamesByCountryMap& LoadForenamesByCountry() = 0;
|
||||
virtual const SurnamesByCountryMap& LoadSurnamesByCountry() = 0;
|
||||
/**
|
||||
* @brief Loads all curated persona records.
|
||||
*/
|
||||
virtual const PersonasList& LoadPersonas() = 0;
|
||||
virtual const ForenamesByCountryMap& LoadForenamesByCountry() = 0;
|
||||
virtual const SurnamesByCountryMap& LoadSurnamesByCountry() = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_CURATED_DATA_CURATED_DATA_SERVICE_H_
|
||||
|
||||
@@ -16,48 +16,48 @@
|
||||
* CuratedJsonDataService.
|
||||
*/
|
||||
struct CuratedDataFilePaths {
|
||||
std::filesystem::path locations_path;
|
||||
std::filesystem::path personas_path;
|
||||
std::filesystem::path forenames_path;
|
||||
std::filesystem::path surnames_path;
|
||||
std::filesystem::path locations_path;
|
||||
std::filesystem::path personas_path;
|
||||
std::filesystem::path forenames_path;
|
||||
std::filesystem::path surnames_path;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Loads curated location, persona, and name data from JSON files.
|
||||
*/
|
||||
class CuratedJsonDataService final : public ICuratedDataService {
|
||||
struct cache {
|
||||
LocationsList locations;
|
||||
PersonasList personas;
|
||||
ForenamesByCountryMap forenames_by_country;
|
||||
SurnamesByCountryMap surnames_by_country;
|
||||
struct cache {
|
||||
LocationsList locations;
|
||||
PersonasList personas;
|
||||
ForenamesByCountryMap forenames_by_country;
|
||||
SurnamesByCountryMap surnames_by_country;
|
||||
|
||||
cache() = default;
|
||||
~cache() = default;
|
||||
};
|
||||
cache() = default;
|
||||
~cache() = default;
|
||||
};
|
||||
|
||||
CuratedDataFilePaths filepaths_;
|
||||
cache cache_;
|
||||
CuratedDataFilePaths filepaths_;
|
||||
cache cache_;
|
||||
|
||||
public:
|
||||
explicit CuratedJsonDataService(CuratedDataFilePaths filepaths);
|
||||
public:
|
||||
explicit CuratedJsonDataService(CuratedDataFilePaths filepaths);
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON array file and returns all location records.
|
||||
*/
|
||||
const LocationsList& LoadLocations() override;
|
||||
/**
|
||||
* @brief Parses a JSON array file and returns all location records.
|
||||
*/
|
||||
const LocationsList& LoadLocations() override;
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON array file and returns all persona records.
|
||||
*/
|
||||
const PersonasList& LoadPersonas() override;
|
||||
/**
|
||||
* @brief Parses a JSON array file and returns all persona records.
|
||||
*/
|
||||
const PersonasList& LoadPersonas() override;
|
||||
|
||||
const ForenamesByCountryMap& LoadForenamesByCountry() override;
|
||||
const ForenamesByCountryMap& LoadForenamesByCountry() override;
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON file and returns all the forenames per country.
|
||||
*/
|
||||
const SurnamesByCountryMap& LoadSurnamesByCountry() override;
|
||||
/**
|
||||
* @brief Parses a JSON file and returns all the forenames per country.
|
||||
*/
|
||||
const SurnamesByCountryMap& LoadSurnamesByCountry() override;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_JSON_HANDLING_JSON_LOADER_H_
|
||||
|
||||
@@ -17,22 +17,22 @@
|
||||
* place of the JSON fixture files used by JsonLoader.
|
||||
*/
|
||||
class MockCuratedDataService final : public ICuratedDataService {
|
||||
public:
|
||||
MockCuratedDataService();
|
||||
public:
|
||||
MockCuratedDataService();
|
||||
|
||||
const LocationsList& LoadLocations() override;
|
||||
const LocationsList& LoadLocations() override;
|
||||
|
||||
const PersonasList& LoadPersonas() override;
|
||||
const PersonasList& LoadPersonas() override;
|
||||
|
||||
const ForenamesByCountryMap& LoadForenamesByCountry() override;
|
||||
const ForenamesByCountryMap& LoadForenamesByCountry() override;
|
||||
|
||||
const SurnamesByCountryMap& LoadSurnamesByCountry() override;
|
||||
const SurnamesByCountryMap& LoadSurnamesByCountry() override;
|
||||
|
||||
private:
|
||||
LocationsList locations_;
|
||||
PersonasList personas_;
|
||||
ForenamesByCountryMap forenames_by_country_;
|
||||
SurnamesByCountryMap surnames_by_country_;
|
||||
private:
|
||||
LocationsList locations_;
|
||||
PersonasList personas_;
|
||||
ForenamesByCountryMap forenames_by_country_;
|
||||
SurnamesByCountryMap surnames_by_country_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_CURATED_DATA_MOCK_CURATED_DATA_SERVICE_H_
|
||||
|
||||
@@ -14,38 +14,38 @@
|
||||
* @brief Interface for services that persist generated brewery records.
|
||||
*/
|
||||
class IExportService {
|
||||
public:
|
||||
IExportService() = default;
|
||||
virtual ~IExportService() = default;
|
||||
public:
|
||||
IExportService() = default;
|
||||
virtual ~IExportService() = default;
|
||||
|
||||
IExportService(const IExportService&) = delete;
|
||||
IExportService& operator=(const IExportService&) = delete;
|
||||
IExportService(IExportService&&) = delete;
|
||||
IExportService& operator=(IExportService&&) = delete;
|
||||
IExportService(const IExportService&) = delete;
|
||||
IExportService& operator=(const IExportService&) = delete;
|
||||
IExportService(IExportService&&) = delete;
|
||||
IExportService& operator=(IExportService&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Prepares the export destination for a new run.
|
||||
*/
|
||||
virtual void Initialize() = 0;
|
||||
/**
|
||||
* @brief Prepares the export destination for a new run.
|
||||
*/
|
||||
virtual void Initialize() = 0;
|
||||
|
||||
/**
|
||||
* @brief Persists one generated brewery record.
|
||||
*
|
||||
* @param brewery Generated brewery payload to store.
|
||||
*/
|
||||
virtual uint64_t ProcessRecord(const BreweryRecord& brewery) = 0;
|
||||
/**
|
||||
* @brief Persists one generated brewery record.
|
||||
*
|
||||
* @param brewery Generated brewery payload to store.
|
||||
*/
|
||||
virtual uint64_t ProcessRecord(const BreweryRecord& brewery) = 0;
|
||||
|
||||
/**
|
||||
* @brief Persists one generated user record.
|
||||
*
|
||||
* @param user Generated user payload to store.
|
||||
*/
|
||||
virtual uint64_t ProcessRecord(const UserRecord& user) = 0;
|
||||
/**
|
||||
* @brief Persists one generated user record.
|
||||
*
|
||||
* @param user Generated user payload to store.
|
||||
*/
|
||||
virtual uint64_t ProcessRecord(const UserRecord& user) = 0;
|
||||
|
||||
/**
|
||||
* @brief Finalizes the export destination.
|
||||
*/
|
||||
virtual void Finalize() = 0;
|
||||
/**
|
||||
* @brief Finalizes the export destination.
|
||||
*/
|
||||
virtual void Finalize() = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_DATABASE_EXPORT_SERVICE_H_
|
||||
|
||||
@@ -20,54 +20,54 @@
|
||||
* @brief Persists generated brewery records into a fresh SQLite database.
|
||||
*/
|
||||
class SqliteExportService final : public IExportService {
|
||||
public:
|
||||
explicit SqliteExportService(const ApplicationOptions& options);
|
||||
~SqliteExportService() override;
|
||||
public:
|
||||
explicit SqliteExportService(const ApplicationOptions& options);
|
||||
~SqliteExportService() override;
|
||||
|
||||
SqliteExportService(const SqliteExportService&) = delete;
|
||||
SqliteExportService& operator=(const SqliteExportService&) = delete;
|
||||
SqliteExportService(SqliteExportService&&) = delete;
|
||||
SqliteExportService& operator=(SqliteExportService&&) = delete;
|
||||
SqliteExportService(const SqliteExportService&) = delete;
|
||||
SqliteExportService& operator=(const SqliteExportService&) = delete;
|
||||
SqliteExportService(SqliteExportService&&) = delete;
|
||||
SqliteExportService& operator=(SqliteExportService&&) = delete;
|
||||
|
||||
void Initialize() override;
|
||||
uint64_t ProcessRecord(const BreweryRecord& brewery) override;
|
||||
uint64_t ProcessRecord(const UserRecord& user) override;
|
||||
void Finalize() override;
|
||||
void Initialize() override;
|
||||
uint64_t ProcessRecord(const BreweryRecord& brewery) override;
|
||||
uint64_t ProcessRecord(const UserRecord& user) override;
|
||||
void Finalize() override;
|
||||
|
||||
private:
|
||||
using SqliteDatabaseHandle =
|
||||
sqlite_export_service_internal::SqliteDatabaseHandle;
|
||||
using SqliteStatementHandle =
|
||||
sqlite_export_service_internal::SqliteStatementHandle;
|
||||
private:
|
||||
using SqliteDatabaseHandle =
|
||||
sqlite_export_service_internal::SqliteDatabaseHandle;
|
||||
using SqliteStatementHandle =
|
||||
sqlite_export_service_internal::SqliteStatementHandle;
|
||||
|
||||
void InitializeSchema() const;
|
||||
void PrepareStatements();
|
||||
void RollbackAndCloseNoThrow() noexcept;
|
||||
void InitializeSchema() const;
|
||||
void PrepareStatements();
|
||||
void RollbackAndCloseNoThrow() noexcept;
|
||||
|
||||
[[nodiscard]] std::filesystem::path BuildDatabasePath() const;
|
||||
[[nodiscard]] static std::string BuildCityKey(const City& city);
|
||||
[[nodiscard]] std::filesystem::path BuildDatabasePath() const;
|
||||
[[nodiscard]] static std::string BuildCityKey(const City& city);
|
||||
|
||||
/**
|
||||
* @brief Returns the row id for @p city, inserting it first if it has
|
||||
* not already been seen during this run.
|
||||
*
|
||||
* Shared by both ProcessRecord() overloads so brewery and user addresses
|
||||
* referencing the same city resolve to the same row.
|
||||
*/
|
||||
[[nodiscard]] sqlite3_int64 ResolveCityId(const City& city);
|
||||
/**
|
||||
* @brief Returns the row id for @p city, inserting it first if it has
|
||||
* not already been seen during this run.
|
||||
*
|
||||
* Shared by both ProcessRecord() overloads so brewery and user addresses
|
||||
* referencing the same city resolve to the same row.
|
||||
*/
|
||||
[[nodiscard]] sqlite3_int64 ResolveCityId(const City& city);
|
||||
|
||||
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_;
|
||||
SqliteStatementHandle insert_city_stmt_;
|
||||
SqliteStatementHandle insert_brewery_stmt_;
|
||||
SqliteStatementHandle insert_brewery_address_stmt_;
|
||||
SqliteStatementHandle insert_user_stmt_;
|
||||
SqliteStatementHandle insert_user_address_stmt_;
|
||||
bool transaction_open_ = false;
|
||||
std::unordered_map<std::string, sqlite3_int64> city_cache_;
|
||||
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_;
|
||||
SqliteStatementHandle insert_city_stmt_;
|
||||
SqliteStatementHandle insert_brewery_stmt_;
|
||||
SqliteStatementHandle insert_brewery_address_stmt_;
|
||||
SqliteStatementHandle insert_user_stmt_;
|
||||
SqliteStatementHandle insert_user_address_stmt_;
|
||||
bool transaction_open_ = false;
|
||||
std::unordered_map<std::string, sqlite3_int64> city_cache_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_DATABASE_SQLITE_EXPORT_SERVICE_H_
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
namespace sqlite_export_service_internal {
|
||||
|
||||
struct SqliteDatabaseDeleter {
|
||||
void operator()(sqlite3* handle) const noexcept;
|
||||
void operator()(sqlite3* handle) const noexcept;
|
||||
};
|
||||
|
||||
struct SqliteStatementDeleter {
|
||||
void operator()(sqlite3_stmt* statement) const noexcept;
|
||||
void operator()(sqlite3_stmt* statement) const noexcept;
|
||||
};
|
||||
|
||||
using SqliteDatabaseHandle = std::unique_ptr<sqlite3, SqliteDatabaseDeleter>;
|
||||
@@ -28,9 +28,9 @@ using SqliteStatementHandle =
|
||||
// N.B. indices are 1 based.
|
||||
template <typename T>
|
||||
struct BoundParam {
|
||||
int index;
|
||||
T value;
|
||||
std::string_view action;
|
||||
int index;
|
||||
T value;
|
||||
std::string_view action;
|
||||
};
|
||||
|
||||
} // namespace sqlite_export_service_internal
|
||||
|
||||
@@ -152,44 +152,44 @@ INSERT INTO user_addresses (
|
||||
// sqlite3_bind_*() parameter indices are 1-based, matching the "?"
|
||||
// placeholder order in the SQL above.
|
||||
enum CityBindIndex {
|
||||
kCityCityBindIndex = 1,
|
||||
kCityStateProvinceBindIndex,
|
||||
kCityIso31662BindIndex,
|
||||
kCityCountryBindIndex,
|
||||
kCityIso31661BindIndex,
|
||||
kCityLanguagesBindIndex,
|
||||
kCityPostalCodeCountryFormatRegexBindIndex,
|
||||
kCityPostalCodeCityRegexJsonBindIndex,
|
||||
kCityCityBindIndex = 1,
|
||||
kCityStateProvinceBindIndex,
|
||||
kCityIso31662BindIndex,
|
||||
kCityCountryBindIndex,
|
||||
kCityIso31661BindIndex,
|
||||
kCityLanguagesBindIndex,
|
||||
kCityPostalCodeCountryFormatRegexBindIndex,
|
||||
kCityPostalCodeCityRegexJsonBindIndex,
|
||||
};
|
||||
|
||||
enum BreweryBindIndex {
|
||||
kBreweryEnglishNameBindIndex = 1,
|
||||
kBreweryEnglishDescriptionBindIndex,
|
||||
kBreweryLocalNameBindIndex,
|
||||
kBreweryLocalDescriptionBindIndex,
|
||||
kBreweryEnglishNameBindIndex = 1,
|
||||
kBreweryEnglishDescriptionBindIndex,
|
||||
kBreweryLocalNameBindIndex,
|
||||
kBreweryLocalDescriptionBindIndex,
|
||||
};
|
||||
|
||||
enum BreweryAddressBindIndex {
|
||||
kBreweryAddressBreweryIdBindIndex = 1,
|
||||
kBreweryAddressCityIdBindIndex,
|
||||
kBreweryAddressPostalCodeBindIndex,
|
||||
kBreweryAddressBreweryIdBindIndex = 1,
|
||||
kBreweryAddressCityIdBindIndex,
|
||||
kBreweryAddressPostalCodeBindIndex,
|
||||
};
|
||||
|
||||
enum UserBindIndex {
|
||||
kUserFirstNameBindIndex = 1,
|
||||
kUserLastNameBindIndex,
|
||||
kUserGenderBindIndex,
|
||||
kUserUsernameBindIndex,
|
||||
kUserBioBindIndex,
|
||||
kUserActivityWeightBindIndex,
|
||||
kUserEmailBindIndex,
|
||||
kUserDateOfBirthBindIndex,
|
||||
kUserFirstNameBindIndex = 1,
|
||||
kUserLastNameBindIndex,
|
||||
kUserGenderBindIndex,
|
||||
kUserUsernameBindIndex,
|
||||
kUserBioBindIndex,
|
||||
kUserActivityWeightBindIndex,
|
||||
kUserEmailBindIndex,
|
||||
kUserDateOfBirthBindIndex,
|
||||
};
|
||||
|
||||
enum UserAddressBindIndex {
|
||||
kUserAddressUserIdBindIndex = 1,
|
||||
kUserAddressCityIdBindIndex,
|
||||
kUserAddressPostalCodeBindIndex,
|
||||
kUserAddressUserIdBindIndex = 1,
|
||||
kUserAddressCityIdBindIndex,
|
||||
kUserAddressPostalCodeBindIndex,
|
||||
};
|
||||
|
||||
SqliteStatementHandle PrepareStatement(const SqliteDatabaseHandle& db_handle,
|
||||
|
||||
@@ -20,49 +20,49 @@
|
||||
* directly
|
||||
*/
|
||||
class IDateTimeProvider {
|
||||
public:
|
||||
virtual ~IDateTimeProvider() = default;
|
||||
public:
|
||||
virtual ~IDateTimeProvider() = default;
|
||||
|
||||
IDateTimeProvider() = default;
|
||||
IDateTimeProvider(const IDateTimeProvider&) = delete;
|
||||
IDateTimeProvider& operator=(const IDateTimeProvider&) = delete;
|
||||
IDateTimeProvider(IDateTimeProvider&&) = delete;
|
||||
IDateTimeProvider& operator=(IDateTimeProvider&&) = delete;
|
||||
IDateTimeProvider() = default;
|
||||
IDateTimeProvider(const IDateTimeProvider&) = delete;
|
||||
IDateTimeProvider& operator=(const IDateTimeProvider&) = delete;
|
||||
IDateTimeProvider(IDateTimeProvider&&) = delete;
|
||||
IDateTimeProvider& operator=(IDateTimeProvider&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Returns the current UTC timestamp in a file-safe format.
|
||||
*
|
||||
* @return UTC timestamp string.
|
||||
*/
|
||||
virtual std::string GetUtcTimestamp() = 0;
|
||||
/**
|
||||
* @brief Returns the current UTC timestamp in a file-safe format.
|
||||
*
|
||||
* @return UTC timestamp string.
|
||||
*/
|
||||
virtual std::string GetUtcTimestamp() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Timestamp provider backed by the system clock.
|
||||
*/
|
||||
class SystemDateTimeProvider final : public IDateTimeProvider {
|
||||
public:
|
||||
std::string GetUtcTimestamp() override {
|
||||
constexpr int kFractionalSecondWidth = 6;
|
||||
public:
|
||||
std::string GetUtcTimestamp() override {
|
||||
constexpr int kFractionalSecondWidth = 6;
|
||||
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto now_time = std::chrono::system_clock::to_time_t(now);
|
||||
const std::tm* utc_time_ptr = std::gmtime(&now_time);
|
||||
if (utc_time_ptr == nullptr) {
|
||||
throw std::runtime_error("Failed to format UTC timestamp");
|
||||
}
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto now_time = std::chrono::system_clock::to_time_t(now);
|
||||
const std::tm* utc_time_ptr = std::gmtime(&now_time);
|
||||
if (utc_time_ptr == nullptr) {
|
||||
throw std::runtime_error("Failed to format UTC timestamp");
|
||||
}
|
||||
|
||||
const auto fractional_seconds =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
now.time_since_epoch()) %
|
||||
std::chrono::seconds(1);
|
||||
const auto fractional_seconds =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
now.time_since_epoch()) %
|
||||
std::chrono::seconds(1);
|
||||
|
||||
std::ostringstream output;
|
||||
output << std::put_time(utc_time_ptr, "%Y-%m-%dT%H-%M-%S");
|
||||
output << '.' << std::setw(kFractionalSecondWidth) << std::setfill('0')
|
||||
<< fractional_seconds.count() << 'Z';
|
||||
return output.str();
|
||||
}
|
||||
std::ostringstream output;
|
||||
output << std::put_time(utc_time_ptr, "%Y-%m-%dT%H-%M-%S");
|
||||
output << '.' << std::setw(kFractionalSecondWidth) << std::setfill('0')
|
||||
<< fractional_seconds.count() << 'Z';
|
||||
return output.str();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_DATETIME_DATE_TIME_PROVIDER_H_
|
||||
|
||||
@@ -8,28 +8,28 @@
|
||||
* @brief Simple timer utility for measuring elapsed time.
|
||||
*/
|
||||
class Timer {
|
||||
std::chrono::steady_clock::time_point start_time =
|
||||
std::chrono::steady_clock::now();
|
||||
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;
|
||||
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 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;
|
||||
}
|
||||
[[nodiscard]] int64_t Reset() {
|
||||
auto previous_elapsed = Elapsed();
|
||||
start_time = std::chrono::steady_clock::now();
|
||||
return previous_elapsed;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_DATETIME_TIMER_H_
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
* @brief Interface for services that can enrich a location with context.
|
||||
*/
|
||||
class IEnrichmentService {
|
||||
public:
|
||||
virtual ~IEnrichmentService() = default;
|
||||
public:
|
||||
virtual ~IEnrichmentService() = default;
|
||||
|
||||
/**
|
||||
* @brief Resolves contextual enrichment for a location.
|
||||
*
|
||||
* @param loc City to enrich.
|
||||
* @return Context text, or an empty string if unavailable.
|
||||
*/
|
||||
virtual std::string GetLocationContext(const City& loc) = 0;
|
||||
/**
|
||||
* @brief Resolves contextual enrichment for a location.
|
||||
*
|
||||
* @param loc City to enrich.
|
||||
* @return Context text, or an empty string if unavailable.
|
||||
*/
|
||||
virtual std::string GetLocationContext(const City& loc) = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ENRICHMENT_ENRICHMENT_SERVICE_H_
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
* @brief Enrichment service that returns no context for any location.
|
||||
*/
|
||||
class MockEnrichmentService final : public IEnrichmentService {
|
||||
public:
|
||||
std::string GetLocationContext(const City& /*loc*/) override {
|
||||
return {};
|
||||
}
|
||||
public:
|
||||
std::string GetLocationContext(const City& /*loc*/) override { return {}; }
|
||||
};
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ENRICHMENT_MOCK_ENRICHMENT_H_
|
||||
|
||||
@@ -19,30 +19,30 @@
|
||||
* @brief Provides Wikipedia summary lookups backed by cached raw extracts.
|
||||
*/
|
||||
class WikipediaEnrichmentService final : public IEnrichmentService {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a new Wikipedia service with the provided web client.
|
||||
*/
|
||||
explicit WikipediaEnrichmentService(std::unique_ptr<WebClient> client,
|
||||
std::shared_ptr<ILogger> logger);
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a new Wikipedia service with the provided web client.
|
||||
*/
|
||||
explicit WikipediaEnrichmentService(std::unique_ptr<WebClient> client,
|
||||
std::shared_ptr<ILogger> logger);
|
||||
|
||||
/**
|
||||
* @brief Returns the Wikipedia-derived context for a location.
|
||||
*/
|
||||
[[nodiscard]] std::string GetLocationContext(const City& loc) override;
|
||||
/**
|
||||
* @brief Returns the Wikipedia-derived context for a location.
|
||||
*/
|
||||
[[nodiscard]] std::string GetLocationContext(const City& loc) override;
|
||||
|
||||
private:
|
||||
std::string FetchExtract(std::string_view query);
|
||||
std::unique_ptr<WebClient> client_;
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
/**
|
||||
* @brief Cache for raw Wikipedia query extracts, keyed by query string.
|
||||
*
|
||||
* GetLocationContext() always queries "brewing" and reuses "beer in
|
||||
* {country}" for every city in that country, so caching avoids refetching
|
||||
* the same extract across locations in a run.
|
||||
*/
|
||||
std::unordered_map<std::string, std::string> extract_cache_;
|
||||
private:
|
||||
std::string FetchExtract(std::string_view query);
|
||||
std::unique_ptr<WebClient> client_;
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
/**
|
||||
* @brief Cache for raw Wikipedia query extracts, keyed by query string.
|
||||
*
|
||||
* GetLocationContext() always queries "brewing" and reuses "beer in
|
||||
* {country}" for every city in that country, so caching avoids refetching
|
||||
* the same extract across locations in a run.
|
||||
*/
|
||||
std::unordered_map<std::string, std::string> extract_cache_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_ENRICHMENT_WIKIPEDIA_SERVICE_H_
|
||||
|
||||
@@ -22,32 +22,32 @@
|
||||
* and exit once the channel has been closed and drained.
|
||||
*/
|
||||
class LogDispatcher {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a log dispatcher.
|
||||
*
|
||||
* @param channel Reference to the bounded channel used for log retrieval.
|
||||
*/
|
||||
explicit LogDispatcher(BoundedChannel<LogEntry>& channel);
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a log dispatcher.
|
||||
*
|
||||
* @param channel Reference to the bounded channel used for log retrieval.
|
||||
*/
|
||||
explicit LogDispatcher(BoundedChannel<LogEntry>& channel);
|
||||
|
||||
LogDispatcher(const LogDispatcher&) = delete;
|
||||
LogDispatcher& operator=(const LogDispatcher&) = delete;
|
||||
LogDispatcher(LogDispatcher&&) = delete;
|
||||
LogDispatcher& operator=(LogDispatcher&&) = delete;
|
||||
~LogDispatcher() = default;
|
||||
LogDispatcher(const LogDispatcher&) = delete;
|
||||
LogDispatcher& operator=(const LogDispatcher&) = delete;
|
||||
LogDispatcher(LogDispatcher&&) = delete;
|
||||
LogDispatcher& operator=(LogDispatcher&&) = delete;
|
||||
~LogDispatcher() = default;
|
||||
|
||||
/**
|
||||
* @brief Drain the channel and forward entries to spdlog.
|
||||
*
|
||||
* Intended to be called once on a dedicated thread. The loop returns after
|
||||
* the channel has been closed and all queued entries have been processed.
|
||||
*/
|
||||
void Run();
|
||||
/**
|
||||
* @brief Drain the channel and forward entries to spdlog.
|
||||
*
|
||||
* Intended to be called once on a dedicated thread. The loop returns after
|
||||
* the channel has been closed and all queued entries have been processed.
|
||||
*/
|
||||
void Run();
|
||||
|
||||
private:
|
||||
BoundedChannel<LogEntry>& channel_;
|
||||
private:
|
||||
BoundedChannel<LogEntry>& channel_;
|
||||
|
||||
static spdlog::level::level_enum ToSpdlogLevel(LogLevel level);
|
||||
static spdlog::level::level_enum ToSpdlogLevel(LogLevel level);
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOG_DISPATCHER_H_
|
||||
|
||||
@@ -20,22 +20,22 @@
|
||||
* @brief Severity levels supported by the logging infra.
|
||||
*/
|
||||
enum class LogLevel {
|
||||
/**
|
||||
* @brief Development/debugging information.
|
||||
*/
|
||||
Debug,
|
||||
/**
|
||||
* @brief General informational messages.
|
||||
*/
|
||||
Info,
|
||||
/**
|
||||
* @brief Warning conditions.
|
||||
*/
|
||||
Warn,
|
||||
/**
|
||||
* @brief Error conditions.
|
||||
*/
|
||||
Error,
|
||||
/**
|
||||
* @brief Development/debugging information.
|
||||
*/
|
||||
Debug,
|
||||
/**
|
||||
* @brief General informational messages.
|
||||
*/
|
||||
Info,
|
||||
/**
|
||||
* @brief Warning conditions.
|
||||
*/
|
||||
Warn,
|
||||
/**
|
||||
* @brief Error conditions.
|
||||
*/
|
||||
Error,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -46,38 +46,38 @@ enum class LogLevel {
|
||||
* pipeline that emitted it.
|
||||
*/
|
||||
enum class PipelinePhase {
|
||||
/**
|
||||
* @brief Initialization and validation.
|
||||
*/
|
||||
Startup,
|
||||
/**
|
||||
* @brief City/context enrichment (e.g. Wikipedia lookups).
|
||||
*/
|
||||
Enrichment,
|
||||
/**
|
||||
* @brief User profile generation.
|
||||
*/
|
||||
UserGeneration,
|
||||
/**
|
||||
* @brief Brewery and beer data generation.
|
||||
*/
|
||||
BreweryAndBeerGeneration,
|
||||
/**
|
||||
* @brief Checkin (visit) record generation.
|
||||
*/
|
||||
CheckinGeneration,
|
||||
/**
|
||||
* @brief Rating and review generation.
|
||||
*/
|
||||
RatingGeneration,
|
||||
/**
|
||||
* @brief Follow relationship generation.
|
||||
*/
|
||||
FollowGeneration,
|
||||
/**
|
||||
* @brief Finalization and cleanup.
|
||||
*/
|
||||
Teardown,
|
||||
/**
|
||||
* @brief Initialization and validation.
|
||||
*/
|
||||
Startup,
|
||||
/**
|
||||
* @brief City/context enrichment (e.g. Wikipedia lookups).
|
||||
*/
|
||||
Enrichment,
|
||||
/**
|
||||
* @brief User profile generation.
|
||||
*/
|
||||
UserGeneration,
|
||||
/**
|
||||
* @brief Brewery and beer data generation.
|
||||
*/
|
||||
BreweryAndBeerGeneration,
|
||||
/**
|
||||
* @brief Checkin (visit) record generation.
|
||||
*/
|
||||
CheckinGeneration,
|
||||
/**
|
||||
* @brief Rating and review generation.
|
||||
*/
|
||||
RatingGeneration,
|
||||
/**
|
||||
* @brief Follow relationship generation.
|
||||
*/
|
||||
FollowGeneration,
|
||||
/**
|
||||
* @brief Finalization and cleanup.
|
||||
*/
|
||||
Teardown,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -86,9 +86,9 @@ enum class PipelinePhase {
|
||||
* transparently.
|
||||
*/
|
||||
struct LogDTO {
|
||||
LogLevel level;
|
||||
PipelinePhase phase;
|
||||
std::string message;
|
||||
LogLevel level;
|
||||
PipelinePhase phase;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -102,35 +102,35 @@ struct LogDTO {
|
||||
* before the entry is dispatched.
|
||||
*/
|
||||
struct LogEntry {
|
||||
/**
|
||||
* @brief Timestamp when the entry was created.
|
||||
*/
|
||||
std::chrono::system_clock::time_point timestamp{};
|
||||
/**
|
||||
* @brief Timestamp when the entry was created.
|
||||
*/
|
||||
std::chrono::system_clock::time_point timestamp{};
|
||||
|
||||
/**
|
||||
* @brief Source location where the log call was made.
|
||||
*/
|
||||
std::source_location origin{};
|
||||
/**
|
||||
* @brief Source location where the log call was made.
|
||||
*/
|
||||
std::source_location origin{};
|
||||
|
||||
/**
|
||||
* @brief Thread responsible for emitting the log.
|
||||
*/
|
||||
std::thread::id thread_id{};
|
||||
/**
|
||||
* @brief Thread responsible for emitting the log.
|
||||
*/
|
||||
std::thread::id thread_id{};
|
||||
|
||||
/**
|
||||
* @brief Severity level of this entry.
|
||||
*/
|
||||
LogLevel level;
|
||||
/**
|
||||
* @brief Severity level of this entry.
|
||||
*/
|
||||
LogLevel level;
|
||||
|
||||
/**
|
||||
* @brief Pipeline phase associated with the entry.
|
||||
*/
|
||||
PipelinePhase phase;
|
||||
/**
|
||||
* @brief Pipeline phase associated with the entry.
|
||||
*/
|
||||
PipelinePhase phase;
|
||||
|
||||
/**
|
||||
* @brief Log message text.
|
||||
*/
|
||||
std::string message;
|
||||
/**
|
||||
* @brief Log message text.
|
||||
*/
|
||||
std::string message;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOG_ENTRY_H_
|
||||
|
||||
@@ -23,31 +23,31 @@
|
||||
* remain valid for the lifetime of the producer.
|
||||
*/
|
||||
class LogProducer final : public ILogger {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a channel-backed producer.
|
||||
*
|
||||
* @param channel Reference to the bounded channel used for log transfer.
|
||||
*/
|
||||
explicit LogProducer(BoundedChannel<LogEntry>& channel);
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a channel-backed producer.
|
||||
*
|
||||
* @param channel Reference to the bounded channel used for log transfer.
|
||||
*/
|
||||
explicit LogProducer(BoundedChannel<LogEntry>& channel);
|
||||
|
||||
LogProducer(const LogProducer&) = delete;
|
||||
LogProducer& operator=(const LogProducer&) = delete;
|
||||
LogProducer(LogProducer&&) = delete;
|
||||
LogProducer& operator=(LogProducer&&) = delete;
|
||||
LogProducer(const LogProducer&) = delete;
|
||||
LogProducer& operator=(const LogProducer&) = delete;
|
||||
LogProducer(LogProducer&&) = delete;
|
||||
LogProducer& operator=(LogProducer&&) = delete;
|
||||
|
||||
~LogProducer() override = default;
|
||||
~LogProducer() override = default;
|
||||
|
||||
/**
|
||||
* @brief Queue a log message for asynchronous processing.
|
||||
*
|
||||
* Blocks while the channel applies backpressure. This blocking behavior
|
||||
* under heavy load is an accepted trade-off for simplicity.
|
||||
*/
|
||||
void DoLog(LogEntry log_entry) override;
|
||||
/**
|
||||
* @brief Queue a log message for asynchronous processing.
|
||||
*
|
||||
* Blocks while the channel applies backpressure. This blocking behavior
|
||||
* under heavy load is an accepted trade-off for simplicity.
|
||||
*/
|
||||
void DoLog(LogEntry log_entry) override;
|
||||
|
||||
private:
|
||||
BoundedChannel<LogEntry>& channel_;
|
||||
private:
|
||||
BoundedChannel<LogEntry>& channel_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOG_PRODUCER_H_
|
||||
|
||||
@@ -23,43 +23,43 @@
|
||||
* by the composition root and injected into services that emit diagnostics.
|
||||
*/
|
||||
class ILogger {
|
||||
public:
|
||||
ILogger() = default;
|
||||
ILogger(const ILogger&) = delete;
|
||||
ILogger& operator=(const ILogger&) = delete;
|
||||
ILogger(ILogger&&) = delete;
|
||||
ILogger& operator=(ILogger&&) = delete;
|
||||
virtual ~ILogger() = default;
|
||||
public:
|
||||
ILogger() = default;
|
||||
ILogger(const ILogger&) = delete;
|
||||
ILogger& operator=(const ILogger&) = delete;
|
||||
ILogger(ILogger&&) = delete;
|
||||
ILogger& operator=(ILogger&&) = delete;
|
||||
virtual ~ILogger() = default;
|
||||
|
||||
/**
|
||||
* @brief Submit a log message to the logging subsystem.
|
||||
*
|
||||
* @param payload User-provided log data (level, phase, message).
|
||||
* @param origin Auto-captured source location of the call site.
|
||||
*/
|
||||
void Log(LogDTO payload,
|
||||
std::source_location origin = std::source_location::current(),
|
||||
std::chrono::system_clock::time_point timestamp =
|
||||
std::chrono::system_clock::now(),
|
||||
std::thread::id thread_id = std::this_thread::get_id()) {
|
||||
LogEntry entry;
|
||||
entry.timestamp = timestamp;
|
||||
entry.thread_id = thread_id;
|
||||
entry.level = payload.level;
|
||||
entry.phase = payload.phase;
|
||||
entry.message = std::move(payload.message);
|
||||
entry.origin = origin;
|
||||
DoLog(std::move(entry));
|
||||
}
|
||||
/**
|
||||
* @brief Submit a log message to the logging subsystem.
|
||||
*
|
||||
* @param payload User-provided log data (level, phase, message).
|
||||
* @param origin Auto-captured source location of the call site.
|
||||
*/
|
||||
void Log(LogDTO payload,
|
||||
std::source_location origin = std::source_location::current(),
|
||||
std::chrono::system_clock::time_point timestamp =
|
||||
std::chrono::system_clock::now(),
|
||||
std::thread::id thread_id = std::this_thread::get_id()) {
|
||||
LogEntry entry;
|
||||
entry.timestamp = timestamp;
|
||||
entry.thread_id = thread_id;
|
||||
entry.level = payload.level;
|
||||
entry.phase = payload.phase;
|
||||
entry.message = std::move(payload.message);
|
||||
entry.origin = origin;
|
||||
DoLog(std::move(entry));
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Underlying implementation to transport the log entry.
|
||||
*
|
||||
* Implementations must be thread-safe as DoLog can be called concurrently
|
||||
* from multiple worker threads.
|
||||
*/
|
||||
virtual void DoLog(LogEntry log_entry) = 0;
|
||||
protected:
|
||||
/**
|
||||
* @brief Underlying implementation to transport the log entry.
|
||||
*
|
||||
* Implementations must be thread-safe as DoLog can be called concurrently
|
||||
* from multiple worker threads.
|
||||
*/
|
||||
virtual void DoLog(LogEntry log_entry) = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOGGER_H_
|
||||
|
||||
@@ -24,56 +24,57 @@
|
||||
* example that fails validation, throws so the caller can skip the city.
|
||||
*/
|
||||
class MockPostalCodeService final : public IPostalCodeService {
|
||||
public:
|
||||
std::string GeneratePostalCode(const City& city) override {
|
||||
const PostalCodeSpec& spec = city.postal_code;
|
||||
public:
|
||||
std::string GeneratePostalCode(const City& city) override {
|
||||
const PostalCodeSpec& spec = city.postal_code;
|
||||
|
||||
if (spec.examples.empty()) {
|
||||
throw std::runtime_error(std::format(
|
||||
"No postal-code examples available for city '{}'", city.city));
|
||||
}
|
||||
|
||||
const std::string& candidate = spec.examples.front();
|
||||
|
||||
if (!MatchesAnyPattern(candidate, spec)) {
|
||||
throw std::runtime_error(
|
||||
std::format("Postal code '{}' for city '{}' does not match the "
|
||||
"curated postal-code regex",
|
||||
candidate, city.city));
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Returns true if @p candidate matches any city regex, or the
|
||||
* country format regex when no city regex matches. When the spec carries no
|
||||
* usable regex at all, validation is skipped (returns true).
|
||||
*/
|
||||
static bool MatchesAnyPattern(const std::string& candidate,
|
||||
const PostalCodeSpec& spec) {
|
||||
bool has_pattern = false;
|
||||
|
||||
for (const std::string& pattern : spec.city_regexes) {
|
||||
if (pattern.empty()) {
|
||||
continue;
|
||||
if (spec.examples.empty()) {
|
||||
throw std::runtime_error(std::format(
|
||||
"No postal-code examples available for city '{}'", city.city));
|
||||
}
|
||||
has_pattern = true;
|
||||
if (std::regex_match(candidate, std::regex(pattern))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!spec.country_format_regex.empty()) {
|
||||
has_pattern = true;
|
||||
if (std::regex_match(candidate, std::regex(spec.country_format_regex))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const std::string& candidate = spec.examples.front();
|
||||
|
||||
return !has_pattern;
|
||||
}
|
||||
if (!MatchesAnyPattern(candidate, spec)) {
|
||||
throw std::runtime_error(
|
||||
std::format("Postal code '{}' for city '{}' does not match the "
|
||||
"curated postal-code regex",
|
||||
candidate, city.city));
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Returns true if @p candidate matches any city regex, or the
|
||||
* country format regex when no city regex matches. When the spec carries no
|
||||
* usable regex at all, validation is skipped (returns true).
|
||||
*/
|
||||
static bool MatchesAnyPattern(const std::string& candidate,
|
||||
const PostalCodeSpec& spec) {
|
||||
bool has_pattern = false;
|
||||
|
||||
for (const std::string& pattern : spec.city_regexes) {
|
||||
if (pattern.empty()) {
|
||||
continue;
|
||||
}
|
||||
has_pattern = true;
|
||||
if (std::regex_match(candidate, std::regex(pattern))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!spec.country_format_regex.empty()) {
|
||||
has_pattern = true;
|
||||
if (std::regex_match(candidate,
|
||||
std::regex(spec.country_format_regex))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !has_pattern;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_MOCK_POSTAL_CODE_SERVICE_H_
|
||||
|
||||
@@ -14,22 +14,22 @@
|
||||
* @brief Interface for services that resolve a postal code for a city.
|
||||
*/
|
||||
class IPostalCodeService {
|
||||
public:
|
||||
IPostalCodeService() = default;
|
||||
virtual ~IPostalCodeService() = default;
|
||||
public:
|
||||
IPostalCodeService() = default;
|
||||
virtual ~IPostalCodeService() = default;
|
||||
|
||||
IPostalCodeService(const IPostalCodeService&) = delete;
|
||||
IPostalCodeService& operator=(const IPostalCodeService&) = delete;
|
||||
IPostalCodeService(IPostalCodeService&&) = delete;
|
||||
IPostalCodeService& operator=(IPostalCodeService&&) = delete;
|
||||
IPostalCodeService(const IPostalCodeService&) = delete;
|
||||
IPostalCodeService& operator=(const IPostalCodeService&) = delete;
|
||||
IPostalCodeService(IPostalCodeService&&) = delete;
|
||||
IPostalCodeService& operator=(IPostalCodeService&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Resolves a postal code for @p city.
|
||||
*
|
||||
* @param city City whose curated postal-code examples supply the result.
|
||||
* @return A postal code string, or an empty string if none is available.
|
||||
*/
|
||||
virtual std::string GeneratePostalCode(const City& city) = 0;
|
||||
/**
|
||||
* @brief Resolves a postal code for @p city.
|
||||
*
|
||||
* @param city City whose curated postal-code examples supply the result.
|
||||
* @return A postal code string, or an empty string if none is available.
|
||||
*/
|
||||
virtual std::string GeneratePostalCode(const City& city) = 0;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_POSTAL_CODE_SERVICE_H_
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
* via GenerateStringFromRegex().
|
||||
*/
|
||||
class XegerPostalCodeService final : public IPostalCodeService {
|
||||
public:
|
||||
XegerPostalCodeService();
|
||||
public:
|
||||
XegerPostalCodeService();
|
||||
|
||||
std::string GeneratePostalCode(const City& city) override;
|
||||
std::string GeneratePostalCode(const City& city) override;
|
||||
|
||||
private:
|
||||
std::mt19937 rng_;
|
||||
private:
|
||||
std::mt19937 rng_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_POSTAL_CODE_XEGER_POSTAL_CODE_SERVICE_H_
|
||||
|
||||
@@ -24,22 +24,22 @@
|
||||
* @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;
|
||||
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 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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -50,33 +50,33 @@ class IPromptDirectory {
|
||||
* 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);
|
||||
PromptDirectory(const std::filesystem::path& prompt_dir,
|
||||
std::shared_ptr<ILogger> logger);
|
||||
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);
|
||||
PromptDirectory(const std::filesystem::path& prompt_dir,
|
||||
std::shared_ptr<ILogger> logger);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
/**
|
||||
* @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::shared_ptr<ILogger> logger_;
|
||||
std::unordered_map<std::string, std::string> cache_;
|
||||
private:
|
||||
std::filesystem::path prompt_dir_;
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
std::unordered_map<std::string, std::string> cache_;
|
||||
};
|
||||
|
||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_PROMPTING_PROMPT_DIRECTORY_H_
|
||||
|
||||
@@ -55,33 +55,33 @@ struct Alternation;
|
||||
struct Repeat;
|
||||
|
||||
struct Visitor {
|
||||
virtual void Visit(const Literal&) = 0;
|
||||
virtual void Visit(const AnyChar&) = 0;
|
||||
virtual void Visit(const CharClass&) = 0;
|
||||
virtual void Visit(const Concat&) = 0;
|
||||
virtual void Visit(const Alternation&) = 0;
|
||||
virtual void Visit(const Repeat&) = 0;
|
||||
virtual ~Visitor() = default;
|
||||
virtual void Visit(const Literal&) = 0;
|
||||
virtual void Visit(const AnyChar&) = 0;
|
||||
virtual void Visit(const CharClass&) = 0;
|
||||
virtual void Visit(const Concat&) = 0;
|
||||
virtual void Visit(const Alternation&) = 0;
|
||||
virtual void Visit(const Repeat&) = 0;
|
||||
virtual ~Visitor() = default;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
virtual void Accept(Visitor&) const = 0;
|
||||
virtual ~Node() = default;
|
||||
virtual void Accept(Visitor&) const = 0;
|
||||
virtual ~Node() = default;
|
||||
};
|
||||
|
||||
using NodePtr = std::unique_ptr<Node>;
|
||||
|
||||
template <typename Derived>
|
||||
struct Visitable : Node {
|
||||
void Accept(Visitor& visitor) const override {
|
||||
visitor.Visit(static_cast<const Derived&>(*this));
|
||||
}
|
||||
void Accept(Visitor& visitor) const override {
|
||||
visitor.Visit(static_cast<const Derived&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
// One fixed character, e.g. 'a'.
|
||||
struct Literal : Visitable<Literal> {
|
||||
char ch;
|
||||
explicit Literal(const char c) : ch(c) {}
|
||||
char ch;
|
||||
explicit Literal(const char c) : ch(c) {}
|
||||
};
|
||||
|
||||
// The "." wildcard: any single printable character.
|
||||
@@ -91,18 +91,18 @@ struct AnyChar : Visitable<AnyChar> {};
|
||||
// character 'x' is the range {'x', 'x'}. `negated` flips the meaning to
|
||||
// "any character NOT listed" (i.e. `[^...]`).
|
||||
struct CharClass : Visitable<CharClass> {
|
||||
std::vector<std::pair<char, char>> ranges;
|
||||
bool negated = false;
|
||||
std::vector<std::pair<char, char>> ranges;
|
||||
bool negated = false;
|
||||
};
|
||||
|
||||
// "Do these children one after another."
|
||||
struct Concat : Visitable<Concat> {
|
||||
std::vector<NodePtr> parts;
|
||||
std::vector<NodePtr> parts;
|
||||
};
|
||||
|
||||
// "Pick exactly one of these options at random" -- the `|` operator.
|
||||
struct Alternation : Visitable<Alternation> {
|
||||
std::vector<NodePtr> options;
|
||||
std::vector<NodePtr> options;
|
||||
};
|
||||
|
||||
// "Repeat one child between min and max times."
|
||||
@@ -111,9 +111,9 @@ struct Alternation : Visitable<Alternation> {
|
||||
// a? -> min 0, max 1
|
||||
// a{2,5} -> min 2, max 5
|
||||
struct Repeat : Visitable<Repeat> {
|
||||
NodePtr child;
|
||||
unsigned int min = 0;
|
||||
std::optional<unsigned int> max; // std::nullopt means "no upper bound"
|
||||
NodePtr child;
|
||||
unsigned int min = 0;
|
||||
std::optional<unsigned int> max; // std::nullopt means "no upper bound"
|
||||
};
|
||||
|
||||
// Parser (recursive descent)
|
||||
@@ -125,75 +125,75 @@ struct Repeat : Visitable<Repeat> {
|
||||
// ParseAtom ::= a . [..] (..) \d
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
explicit Parser(std::string_view pattern);
|
||||
public:
|
||||
explicit Parser(std::string_view pattern);
|
||||
|
||||
NodePtr Parse();
|
||||
NodePtr Parse();
|
||||
|
||||
private:
|
||||
std::string src_;
|
||||
size_t pos_ = 0;
|
||||
private:
|
||||
std::string src_;
|
||||
size_t pos_ = 0;
|
||||
|
||||
// Strips anchors (leading '^' and trailing '$') as they are not needed for
|
||||
// generation.
|
||||
static std::string StripAnchors(std::string_view pattern);
|
||||
// Strips anchors (leading '^' and trailing '$') as they are not needed for
|
||||
// generation.
|
||||
static std::string StripAnchors(std::string_view pattern);
|
||||
|
||||
[[nodiscard]] bool AtEnd() const;
|
||||
[[nodiscard]] char Peek() const;
|
||||
char Advance();
|
||||
bool Match(char c);
|
||||
void Expect(char c);
|
||||
[[nodiscard]] bool AtEnd() const;
|
||||
[[nodiscard]] char Peek() const;
|
||||
char Advance();
|
||||
bool Match(char c);
|
||||
void Expect(char c);
|
||||
|
||||
// Level 1: alternation -- ( concat | concat | concat )
|
||||
NodePtr ParseAlternation();
|
||||
// Level 1: alternation -- ( concat | concat | concat )
|
||||
NodePtr ParseAlternation();
|
||||
|
||||
// Level 2: concatenation -- a run of items back to back
|
||||
NodePtr ParseConcat();
|
||||
// Level 2: concatenation -- a run of items back to back
|
||||
NodePtr ParseConcat();
|
||||
|
||||
// Level 3: an atom plus an optional quantifier
|
||||
NodePtr ParseRepeat();
|
||||
// Level 3: an atom plus an optional quantifier
|
||||
NodePtr ParseRepeat();
|
||||
|
||||
// Curly-brace quantifiers: {n}, {n,}, {n,m}
|
||||
NodePtr ParseBrace(NodePtr atom);
|
||||
// Curly-brace quantifiers: {n}, {n,}, {n,m}
|
||||
NodePtr ParseBrace(NodePtr atom);
|
||||
|
||||
// Level 4: an atom -- the smallest standalone unit
|
||||
NodePtr ParseAtom();
|
||||
// Level 4: an atom -- the smallest standalone unit
|
||||
NodePtr ParseAtom();
|
||||
|
||||
static NodePtr MakeClass(std::vector<std::pair<char, char>> ranges,
|
||||
bool negated);
|
||||
static NodePtr MakeClass(std::vector<std::pair<char, char>> ranges,
|
||||
bool negated);
|
||||
|
||||
NodePtr ParseEscape();
|
||||
NodePtr ParseEscape();
|
||||
|
||||
NodePtr ParseCharClass();
|
||||
NodePtr ParseCharClass();
|
||||
|
||||
// Reads one character inside [ ], turning \] \n etc. into the real
|
||||
// character they represent.
|
||||
char ClassChar();
|
||||
// Reads one character inside [ ], turning \] \n etc. into the real
|
||||
// character they represent.
|
||||
char ClassChar();
|
||||
};
|
||||
|
||||
// Generator -- a Visitor that emits a random matching string
|
||||
|
||||
class XegerGenerator final : public Visitor {
|
||||
public:
|
||||
XegerGenerator(std::mt19937& rng, int unbounded_cap);
|
||||
public:
|
||||
XegerGenerator(std::mt19937& rng, int unbounded_cap);
|
||||
|
||||
std::string Run(const Node& root);
|
||||
std::string Run(const Node& root);
|
||||
|
||||
void Visit(const Literal& node) override;
|
||||
void Visit(const AnyChar&) override;
|
||||
void Visit(const CharClass& node) override;
|
||||
void Visit(const Concat& node) override;
|
||||
void Visit(const Alternation& node) override;
|
||||
void Visit(const Repeat& node) override;
|
||||
void Visit(const Literal& node) override;
|
||||
void Visit(const AnyChar&) override;
|
||||
void Visit(const CharClass& node) override;
|
||||
void Visit(const Concat& node) override;
|
||||
void Visit(const Alternation& node) override;
|
||||
void Visit(const Repeat& node) override;
|
||||
|
||||
private:
|
||||
std::mt19937& rng_;
|
||||
int unbounded_cap_;
|
||||
std::string out_;
|
||||
private:
|
||||
std::mt19937& rng_;
|
||||
int unbounded_cap_;
|
||||
std::string out_;
|
||||
|
||||
static bool InRanges(const CharClass& node, char c);
|
||||
static bool InRanges(const CharClass& node, char c);
|
||||
|
||||
char PickFromClass(const CharClass& node);
|
||||
char PickFromClass(const CharClass& node);
|
||||
};
|
||||
|
||||
} // namespace xeger_internal
|
||||
|
||||
Reference in New Issue
Block a user