/// Domain-only repository contracts for scheduler persistence adapters. /// /// This package defines the stable boundary that SQLite, memory, and future /// adapters must implement. Interfaces accept and return domain objects from /// `scheduler_core`; storage rows, Drift classes, SQL clients, and platform /// details stay behind adapter packages. library; import 'package:scheduler_core/scheduler_core.dart' as core; /// Adapter-neutral repository failure. sealed class RepositoryFailure { const RepositoryFailure({ this.entityId, this.expectedRevision, this.actualRevision, this.message, }); /// Stable id of the entity related to the failure, when available. final String? entityId; /// Revision the caller expected for a compare-and-set operation. final core.Revision? expectedRevision; /// Revision currently stored by the adapter. final core.Revision? actualRevision; /// Optional non-localized diagnostic text for logs and tests. final String? message; } /// The requested entity was not found. final class RepositoryNotFound extends RepositoryFailure { /// Creates a not-found failure for [entityId]. const RepositoryNotFound({super.entityId, super.message}); } /// A record with the requested id already exists. final class RepositoryDuplicateId extends RepositoryFailure { /// Creates a duplicate-id failure for [entityId]. const RepositoryDuplicateId({super.entityId, super.message}); } /// The caller supplied an invalid revision value. final class RepositoryInvalidRevision extends RepositoryFailure { /// Creates an invalid-revision failure. const RepositoryInvalidRevision({ super.entityId, super.expectedRevision, super.message, }); } /// The caller attempted to write using a stale revision. final class RepositoryStaleRevision extends RepositoryFailure { /// Creates a stale-revision failure. const RepositoryStaleRevision({ super.entityId, required super.expectedRevision, required super.actualRevision, super.message, }); } /// The requested record exists under a different owner scope. final class RepositoryOwnerMismatch extends RepositoryFailure { /// Creates an owner-mismatch failure for [entityId]. const RepositoryOwnerMismatch({super.entityId, super.message}); } /// The adapter could not complete the operation due to storage failure. final class RepositoryStorageFailure extends RepositoryFailure { /// Creates a storage failure with optional [message]. const RepositoryStorageFailure({super.entityId, super.message}); } /// Minimal Either type for expected repository success/failure paths. sealed class Either { const Either(); /// Whether this value is a left/failure value. bool get isLeft => this is Left; /// Whether this value is a right/success value. bool get isRight => this is Right; /// Return the left value or throw when this is right. L get left => switch (this) { Left(:final value) => value, Right() => throw StateError('Either does not contain left.'), }; /// Return the right value or throw when this is left. R get right => switch (this) { Left() => throw StateError('Either does not contain right.'), Right(:final value) => value, }; } /// Failure side of [Either]. final class Left extends Either { /// Creates a left/failure value. const Left(this.value); /// Wrapped failure value. final L value; } /// Success side of [Either]. final class Right extends Either { /// Creates a right/success value. const Right(this.value); /// Wrapped success value. final R value; } /// Repository result wrapper for expected adapter outcomes. typedef RepoResult = Either; /// Repository contract for task records. abstract interface class TaskRepository { /// Return the task with [taskId] in [ownerId], or null when it is absent. Future> findById({ required core.OwnerId ownerId, required String taskId, }); /// Return owner-scoped tasks in deterministic adapter order. Future>> findByOwner({ required core.OwnerId ownerId, core.PageRequest page = const core.PageRequest(), }); /// Return owner-scoped tasks assigned to [projectId]. Future>> findByProjectId({ required core.OwnerId ownerId, required String projectId, core.PageRequest page = const core.PageRequest(), }); /// Return owner-scoped direct child tasks for [parentTaskId]. Future>> findByParentTaskId({ required core.OwnerId ownerId, required String parentTaskId, core.PageRequest page = const core.PageRequest(), }); /// Return owner-scoped tasks with [status]. Future>> findByStatus({ required core.OwnerId ownerId, required core.TaskStatus status, core.PageRequest page = const core.PageRequest(), }); /// Insert [task] for [ownerId] and return the assigned revision. Future> insert({ required core.OwnerId ownerId, required core.Task task, }); /// Save [task] if [expectedRevision] still matches the stored record. Future> save({ required core.OwnerId ownerId, required core.Task task, required core.Revision expectedRevision, }); /// Remove [taskId] if [expectedRevision] still matches the stored record. Future> delete({ required core.OwnerId ownerId, required String taskId, required core.Revision expectedRevision, }); } /// Repository contract for project profile records. abstract interface class ProjectRepository { /// Return the project with [projectId] in [ownerId], or null when absent. Future> findById({ required core.OwnerId ownerId, required String projectId, }); /// Return owner-scoped projects, optionally including archived profiles. Future>> findByOwner({ required core.OwnerId ownerId, bool includeArchived = false, core.PageRequest page = const core.PageRequest(), }); /// Insert [project] for [ownerId] and return the assigned revision. Future> insert({ required core.OwnerId ownerId, required core.ProjectProfile project, }); /// Save [project] if [expectedRevision] still matches the stored record. Future> save({ required core.OwnerId ownerId, required core.ProjectProfile project, required core.Revision expectedRevision, }); /// Archive [projectId] without deleting task history. Future> archive({ required core.OwnerId ownerId, required String projectId, required core.Revision expectedRevision, required DateTime archivedAtUtc, }); } /// Repository contract for locked blocks and date-scoped overrides. abstract interface class LockedBlockRepository { /// Return the locked block with [blockId] in [ownerId], or null when absent. Future> findBlockById({ required core.OwnerId ownerId, required String blockId, }); /// Return owner-scoped locked blocks. Future>> findBlocksByOwner({ required core.OwnerId ownerId, bool includeArchived = false, core.PageRequest page = const core.PageRequest(), }); /// Return the override with [overrideId] in [ownerId], or null when absent. Future> findOverrideById({ required core.OwnerId ownerId, required String overrideId, }); /// Return owner-scoped overrides for [date]. Future>> findOverridesForDate({ required core.OwnerId ownerId, required core.CivilDate date, core.PageRequest page = const core.PageRequest(), }); /// Insert [block] for [ownerId] and return the assigned revision. Future> insertBlock({ required core.OwnerId ownerId, required core.LockedBlock block, }); /// Save [block] if [expectedRevision] still matches the stored record. Future> saveBlock({ required core.OwnerId ownerId, required core.LockedBlock block, required core.Revision expectedRevision, }); /// Archive [blockId] without deleting historical schedule context. Future> archiveBlock({ required core.OwnerId ownerId, required String blockId, required core.Revision expectedRevision, required DateTime archivedAtUtc, }); /// Insert [override] for [ownerId] and return the assigned revision. Future> insertOverride({ required core.OwnerId ownerId, required core.LockedBlockOverride override, }); /// Save [override] if [expectedRevision] still matches the stored record. Future> saveOverride({ required core.OwnerId ownerId, required core.LockedBlockOverride override, required core.Revision expectedRevision, }); } /// Repository contract for owner settings. abstract interface class SettingsRepository { /// Return settings for [ownerId], or null when no settings have been saved. Future> findByOwner({ required core.OwnerId ownerId, }); /// Insert [settings] for [ownerId] and return the assigned revision. Future> insert({ required core.OwnerId ownerId, required core.OwnerSettings settings, }); /// Save [settings] if [expectedRevision] still matches the stored record. Future> save({ required core.OwnerId ownerId, required core.OwnerSettings settings, required core.Revision expectedRevision, }); } /// Repository contract for diagnostic schedule snapshots. abstract interface class ScheduleSnapshotRepository { /// Return the snapshot with [snapshotId] in [ownerId], or null when absent. Future> findById({ required core.OwnerId ownerId, required String snapshotId, }); /// Return snapshots captured for [ownerId] that overlap [window]. Future>> findInWindow({ required core.OwnerId ownerId, required core.SchedulingWindow window, core.PageRequest page = const core.PageRequest(), }); /// Insert [snapshot] for [ownerId] and return the assigned revision. Future> insert({ required core.OwnerId ownerId, required core.SchedulingStateSnapshot snapshot, }); /// Delete snapshots whose retention expiry is before [nowUtc]. Future> deleteExpired({ required core.OwnerId ownerId, required DateTime nowUtc, }); }