Fist commit
This commit is contained in:
136
lib/timbre_core/TimbreService.h
Executable file
136
lib/timbre_core/TimbreService.h
Executable 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
|
||||
Reference in New Issue
Block a user