Fist commit

This commit is contained in:
Angel Ivan
2026-10-02 12:08:21 -06:00
commit 2a9eabce14
54 changed files with 10737 additions and 0 deletions

136
lib/timbre_core/TimbreService.h Executable file
View File

@@ -0,0 +1,136 @@
#pragma once
#include "ScheduleValidator.h"
#include "StateRepository.h"
#include <limits>
#include <type_traits>
#include <utility>
namespace timbre_core {
enum class ServiceCode : std::uint8_t {
Ok = 0,
NotLoaded,
LoadFailed,
InvalidRepositoryState,
MutationRejected,
ValidationFailed,
CommitFailed,
NotFound,
LimitExceeded,
DuplicateId
};
struct ServiceResult {
bool ok = true;
ServiceCode code = ServiceCode::Ok;
ValidationResult validation{};
constexpr bool succeeded() const noexcept { return ok; }
constexpr explicit operator bool() const noexcept { return ok; }
};
class TimbreService {
public:
explicit TimbreService(StateRepository& repository) : repository_(&repository) {}
bool load();
bool loaded() const noexcept { return loaded_; }
const AppState& state() const noexcept { return ram_; }
const AppState& ram() const noexcept { return ram_; }
const ServiceResult& lastResult() const noexcept { return lastResult_; }
ServiceCode lastError() const noexcept { return lastResult_.code; }
// Copy, mutate, validate, persist, and only then publish to ram_. The
// lambda may return bool (false rejects the transaction) or void.
template <typename Mutator>
bool transact(Mutator&& mutator) {
AppState candidate = ram_;
bool accepted = true;
if constexpr (std::is_void_v<std::invoke_result_t<Mutator, AppState&>>) {
std::forward<Mutator>(mutator)(candidate);
} else {
accepted = static_cast<bool>(
std::forward<Mutator>(mutator)(candidate));
}
if (!accepted) {
return fail(ServiceCode::MutationRejected, ValidationResult{});
}
if (candidate.revision == std::numeric_limits<std::uint32_t>::max()) {
candidate.revision = 0;
} else {
++candidate.revision;
}
const ValidationResult validation = ScheduleValidator::validate(candidate);
if (!validation.valid) {
return fail(ServiceCode::ValidationFailed, validation);
}
if (!repository_->commit(candidate)) {
return fail(ServiceCode::CommitFailed, ValidationResult{});
}
// This is the only assignment to the live model in a transaction.
ram_ = candidate;
loaded_ = true;
lastResult_ = ServiceResult{true, ServiceCode::Ok, ValidationResult{}};
return true;
}
template <typename Mutator>
bool mutate(Mutator&& mutator) {
return transact(std::forward<Mutator>(mutator));
}
template <typename Mutator>
bool apply(Mutator&& mutator) {
return transact(std::forward<Mutator>(mutator));
}
bool replace(const AppState& replacement) {
return transact([&replacement](AppState& candidate) {
candidate = replacement;
return true;
});
}
bool commitCurrent();
bool commit() { return commitCurrent(); }
bool addProfile(const Profile& profile);
bool updateProfile(ProfileId id, const Profile& profile);
bool removeProfile(ProfileId id);
bool deleteProfile(ProfileId id) { return removeProfile(id); }
bool addSchedule(const Schedule& schedule);
bool updateSchedule(ScheduleId id, const Schedule& schedule);
bool removeSchedule(ScheduleId id);
bool deleteSchedule(ScheduleId id) { return removeSchedule(id); }
bool addHoliday(const Holiday& holiday);
bool updateHoliday(HolidayId id, const Holiday& holiday);
bool removeHoliday(HolidayId id);
bool deleteHoliday(HolidayId id) { return removeHoliday(id); }
bool setActiveProfile(ProfileId id);
bool setPattern(PatternId id, const Pattern& pattern);
bool setPatterns(const std::array<Pattern, kMaxPatterns>& patterns);
private:
bool fail(ServiceCode code, const ValidationResult& validation);
void observeIds(const AppState& state) noexcept;
Id nextAvailableProfileId(const AppState& state);
Id nextAvailableScheduleId(const AppState& state);
Id nextAvailableHolidayId(const AppState& state);
StateRepository* repository_ = nullptr;
AppState ram_{};
StableIdAllocator nextProfileId_{};
StableIdAllocator nextScheduleId_{};
StableIdAllocator nextHolidayId_{};
bool loaded_ = false;
ServiceResult lastResult_{};
};
} // namespace timbre_core