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

305
lib/timbre_core/TimbreService.cpp Executable file
View 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([&copy](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([&copy](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([&copy](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([&copy](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([&copy](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([&copy](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