Fist commit
This commit is contained in:
305
lib/timbre_core/TimbreService.cpp
Executable file
305
lib/timbre_core/TimbreService.cpp
Executable file
@@ -0,0 +1,305 @@
|
||||
#include "TimbreService.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace timbre_core {
|
||||
namespace {
|
||||
|
||||
bool containsProfile(const AppState& state, ProfileId id) noexcept {
|
||||
return state.profile(id) != nullptr;
|
||||
}
|
||||
|
||||
bool containsSchedule(const AppState& state, ScheduleId id) noexcept {
|
||||
return state.schedule(id) != nullptr;
|
||||
}
|
||||
|
||||
bool containsHoliday(const AppState& state, HolidayId id) noexcept {
|
||||
return state.holiday(id) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TimbreService::fail(ServiceCode code, const ValidationResult& validation) {
|
||||
lastResult_ = ServiceResult{false, code, validation};
|
||||
return false;
|
||||
}
|
||||
|
||||
void TimbreService::observeIds(const AppState& state) noexcept {
|
||||
nextProfileId_.reset();
|
||||
nextScheduleId_.reset();
|
||||
nextHolidayId_.reset();
|
||||
for (std::size_t i = 0; i < state.profiles.size(); ++i) {
|
||||
nextProfileId_.observe(state.profiles[i].id);
|
||||
}
|
||||
for (std::size_t i = 0; i < state.schedules.size(); ++i) {
|
||||
nextScheduleId_.observe(state.schedules[i].id);
|
||||
}
|
||||
for (std::size_t i = 0; i < state.holidays.size(); ++i) {
|
||||
nextHolidayId_.observe(state.holidays[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
Id TimbreService::nextAvailableProfileId(const AppState& state) {
|
||||
for (;;) {
|
||||
const Id id = nextProfileId_.next();
|
||||
if (id != kInvalidId && !containsProfile(state, id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Id TimbreService::nextAvailableScheduleId(const AppState& state) {
|
||||
for (;;) {
|
||||
const Id id = nextScheduleId_.next();
|
||||
if (id != kInvalidId && !containsSchedule(state, id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Id TimbreService::nextAvailableHolidayId(const AppState& state) {
|
||||
for (;;) {
|
||||
const Id id = nextHolidayId_.next();
|
||||
if (id != kInvalidId && !containsHoliday(state, id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TimbreService::load() {
|
||||
AppState candidate;
|
||||
if (repository_ == nullptr || !repository_->load(candidate)) {
|
||||
return fail(ServiceCode::LoadFailed, ValidationResult{});
|
||||
}
|
||||
const ValidationResult validation = ScheduleValidator::validate(candidate);
|
||||
if (!validation.valid) {
|
||||
return fail(ServiceCode::InvalidRepositoryState, validation);
|
||||
}
|
||||
ram_ = candidate;
|
||||
observeIds(ram_);
|
||||
loaded_ = true;
|
||||
lastResult_ = ServiceResult{true, ServiceCode::Ok, ValidationResult{}};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimbreService::commitCurrent() {
|
||||
const ValidationResult validation = ScheduleValidator::validate(ram_);
|
||||
if (!validation.valid) {
|
||||
return fail(ServiceCode::ValidationFailed, validation);
|
||||
}
|
||||
if (repository_ == nullptr || !repository_->commit(ram_)) {
|
||||
return fail(ServiceCode::CommitFailed, ValidationResult{});
|
||||
}
|
||||
lastResult_ = ServiceResult{true, ServiceCode::Ok, ValidationResult{}};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimbreService::addProfile(const Profile& profile) {
|
||||
if (ram_.profiles.full()) {
|
||||
return fail(ServiceCode::LimitExceeded, ValidationResult{});
|
||||
}
|
||||
const ProfileId id = profile.id == kInvalidId ? nextAvailableProfileId(ram_) : profile.id;
|
||||
if (containsProfile(ram_, id)) {
|
||||
return fail(ServiceCode::DuplicateId, ValidationResult{});
|
||||
}
|
||||
Profile copy = profile;
|
||||
copy.id = id;
|
||||
const StableIdAllocator savedAllocator = nextProfileId_;
|
||||
const bool committed = transact([©](AppState& candidate) {
|
||||
if (!candidate.profiles.push_back(copy)) {
|
||||
return false;
|
||||
}
|
||||
if (candidate.profiles.size() == 1) {
|
||||
candidate.activeProfileId = copy.id;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!committed) {
|
||||
nextProfileId_ = savedAllocator;
|
||||
}
|
||||
return committed;
|
||||
}
|
||||
|
||||
bool TimbreService::updateProfile(ProfileId id, const Profile& profile) {
|
||||
if (!containsProfile(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
Profile copy = profile;
|
||||
copy.id = id;
|
||||
return transact([©](AppState& candidate) {
|
||||
Profile* target = candidate.profile(copy.id);
|
||||
if (target == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*target = copy;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::removeProfile(ProfileId id) {
|
||||
if (!containsProfile(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
return transact([id](AppState& candidate) {
|
||||
std::size_t index = 0;
|
||||
for (; index < candidate.profiles.size(); ++index) {
|
||||
if (candidate.profiles[index].id == id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= candidate.profiles.size() || !candidate.profiles.erase(index)) {
|
||||
return false;
|
||||
}
|
||||
if (candidate.activeProfileId == id) {
|
||||
candidate.activeProfileId = candidate.profiles.empty()
|
||||
? 0
|
||||
: candidate.profiles[0].id;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::addSchedule(const Schedule& schedule) {
|
||||
if (ram_.schedules.full()) {
|
||||
return fail(ServiceCode::LimitExceeded, ValidationResult{});
|
||||
}
|
||||
const ScheduleId id = schedule.id == kInvalidId
|
||||
? nextAvailableScheduleId(ram_)
|
||||
: schedule.id;
|
||||
if (containsSchedule(ram_, id)) {
|
||||
return fail(ServiceCode::DuplicateId, ValidationResult{});
|
||||
}
|
||||
Schedule copy = schedule;
|
||||
copy.id = id;
|
||||
const StableIdAllocator savedAllocator = nextScheduleId_;
|
||||
const bool committed = transact([©](AppState& candidate) {
|
||||
if (!candidate.schedules.push_back(copy)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!committed) {
|
||||
nextScheduleId_ = savedAllocator;
|
||||
}
|
||||
return committed;
|
||||
}
|
||||
|
||||
bool TimbreService::updateSchedule(ScheduleId id, const Schedule& schedule) {
|
||||
if (!containsSchedule(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
Schedule copy = schedule;
|
||||
copy.id = id;
|
||||
return transact([©](AppState& candidate) {
|
||||
Schedule* target = candidate.schedule(copy.id);
|
||||
if (target == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*target = copy;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::removeSchedule(ScheduleId id) {
|
||||
if (!containsSchedule(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
return transact([id](AppState& candidate) {
|
||||
for (std::size_t i = 0; i < candidate.schedules.size(); ++i) {
|
||||
if (candidate.schedules[i].id == id) {
|
||||
return candidate.schedules.erase(i);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::addHoliday(const Holiday& holiday) {
|
||||
if (ram_.holidays.full()) {
|
||||
return fail(ServiceCode::LimitExceeded, ValidationResult{});
|
||||
}
|
||||
const HolidayId id = holiday.id == kInvalidId
|
||||
? nextAvailableHolidayId(ram_)
|
||||
: holiday.id;
|
||||
if (containsHoliday(ram_, id)) {
|
||||
return fail(ServiceCode::DuplicateId, ValidationResult{});
|
||||
}
|
||||
Holiday copy = holiday;
|
||||
copy.id = id;
|
||||
const StableIdAllocator savedAllocator = nextHolidayId_;
|
||||
const bool committed = transact([©](AppState& candidate) {
|
||||
if (!candidate.holidays.push_back(copy)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!committed) {
|
||||
nextHolidayId_ = savedAllocator;
|
||||
}
|
||||
return committed;
|
||||
}
|
||||
|
||||
bool TimbreService::updateHoliday(HolidayId id, const Holiday& holiday) {
|
||||
if (!containsHoliday(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
Holiday copy = holiday;
|
||||
copy.id = id;
|
||||
return transact([©](AppState& candidate) {
|
||||
Holiday* target = candidate.holiday(copy.id);
|
||||
if (target == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*target = copy;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::removeHoliday(HolidayId id) {
|
||||
if (!containsHoliday(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
return transact([id](AppState& candidate) {
|
||||
for (std::size_t i = 0; i < candidate.holidays.size(); ++i) {
|
||||
if (candidate.holidays[i].id == id) {
|
||||
return candidate.holidays.erase(i);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::setActiveProfile(ProfileId id) {
|
||||
if (!containsProfile(ram_, id)) {
|
||||
return fail(ServiceCode::NotFound, ValidationResult{});
|
||||
}
|
||||
return transact([id](AppState& candidate) {
|
||||
candidate.activeProfileId = id;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::setPattern(PatternId patternIdentifier, const Pattern& pattern) {
|
||||
if (!isPatternId(patternIdentifier) ||
|
||||
patternIdentifier != pattern.id ||
|
||||
!ScheduleValidator::validatePattern(pattern).valid) {
|
||||
return fail(ServiceCode::ValidationFailed, ValidationResult{});
|
||||
}
|
||||
return transact([patternIdentifier, pattern](AppState& candidate) {
|
||||
const std::size_t index = patternIndex(patternIdentifier);
|
||||
if (index >= candidate.patterns.size()) {
|
||||
return false;
|
||||
}
|
||||
candidate.patterns[index] = pattern;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TimbreService::setPatterns(const std::array<Pattern, kMaxPatterns>& patterns) {
|
||||
return transact([&patterns](AppState& candidate) {
|
||||
candidate.patterns = patterns;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace timbre_core
|
||||
Reference in New Issue
Block a user