diff --git a/src/MfGames.Nitride.Temporal.Schedules/ApplySchedules.cs b/src/MfGames.Nitride.Temporal.Schedules/ApplySchedules.cs index 82eae2e..de65567 100644 --- a/src/MfGames.Nitride.Temporal.Schedules/ApplySchedules.cs +++ b/src/MfGames.Nitride.Temporal.Schedules/ApplySchedules.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; @@ -8,24 +9,6 @@ using MfGames.Nitride.Generators; namespace MfGames.Nitride.Temporal.Schedules; -public enum SchedulePeriod -{ - /// - /// Indicates that all the matching schedule periods are instant (zero time). - /// - Instant, - - /// - /// Indicates that the entities will be scheduled a day apart. - /// - Day, - - /// - /// Indicates that the entities will be scheduled seven days apart. - /// - Week, -} - /// /// Applies schedules against the list of entities. /// @@ -40,13 +23,15 @@ public partial class ApplySchedules : OperationBase { this.Timekeeper = timekeeper; this.validator = validator; - this.Schedules = new List(); } /// - /// Gets or sets the ordered list of schedules to apply to the entities. + /// Gets or sets the callback to get the schedules for the entity. This is + /// used to allow for per-entity schedules or generic schedules across + /// the entire system. If this returns null, then no schedule will be + /// applied. /// - public IList Schedules { get; set; } + public Func?>? GetSchedules { get; set; } /// /// Gets or sets the timekeeper associated with this operation. @@ -61,16 +46,30 @@ public partial class ApplySchedules : OperationBase return input.Select(this.Apply); } - public ApplySchedules WithSchedules(IEnumerable items) - where TItem : ISchedule + public ApplySchedules WithGetSchedules( + Func?> value) + where TType : ISchedule { - this.Schedules = items.OfType().ToList(); + this.GetSchedules = entity => value? + .Invoke(entity) + ?.Cast() + .ToList(); + return this; } private Entity Apply(Entity entity) { - foreach (ISchedule schedule in this.Schedules) + // Get the schedule for this entity. + IList? schedules = this.GetSchedules?.Invoke(entity); + + if (schedules == null || schedules.Count == 0) + { + return entity; + } + + // Otherwise, apply the schedules to this entity. + foreach (ISchedule schedule in schedules) { if (!schedule.CanApply(entity)) { diff --git a/src/MfGames.Nitride.Temporal.Schedules/NumericalPathSchedule.cs b/src/MfGames.Nitride.Temporal.Schedules/NumericalPathSchedule.cs index cd95618..bfcaea5 100644 --- a/src/MfGames.Nitride.Temporal.Schedules/NumericalPathSchedule.cs +++ b/src/MfGames.Nitride.Temporal.Schedules/NumericalPathSchedule.cs @@ -20,7 +20,7 @@ public partial class NumericalPathSchedule : ISchedule public NumericalPathSchedule() { this.PathRegex = new Regex(@"^.*(\d+)"); - this.GetPath = (entity) => entity.Get().ToString(); + this.GetPath = entity => entity.Get().ToString(); this.CaptureGroup = 1; this.CaptureOffset = -1; } diff --git a/src/MfGames.Nitride.Temporal.Schedules/SchedulePeriod.cs b/src/MfGames.Nitride.Temporal.Schedules/SchedulePeriod.cs new file mode 100644 index 0000000..d3686fd --- /dev/null +++ b/src/MfGames.Nitride.Temporal.Schedules/SchedulePeriod.cs @@ -0,0 +1,19 @@ +namespace MfGames.Nitride.Temporal.Schedules; + +public enum SchedulePeriod +{ + /// + /// Indicates that all the matching schedule periods are instant (zero time). + /// + Instant, + + /// + /// Indicates that the entities will be scheduled a day apart. + /// + Day, + + /// + /// Indicates that the entities will be scheduled seven days apart. + /// + Week, +} diff --git a/src/MfGames.Nitride.Temporal.Schedules/SimplePathSchedule.cs b/src/MfGames.Nitride.Temporal.Schedules/SimplePathSchedule.cs new file mode 100644 index 0000000..1d23030 --- /dev/null +++ b/src/MfGames.Nitride.Temporal.Schedules/SimplePathSchedule.cs @@ -0,0 +1,51 @@ +using System; + +using MfGames.Gallium; +using MfGames.Nitride.Generators; + +using NodaTime; + +namespace MfGames.Nitride.Temporal.Schedules; + +/// +/// A schedule that goes against all entities it is applied to. +/// +[WithProperties] +public partial class SimplePathSchedule : ISchedule +{ + /// + /// Gets or sets when the first item is scheduled. + /// + public DateTime? ScheduleStart { get; set; } + + /// + public virtual Entity Apply( + Entity entity, + Timekeeper timekeeper) + { + DateTime start = this.ScheduleStart + ?? throw new NullReferenceException( + "Cannot use a schedule without a start date."); + Instant instant = timekeeper.CreateInstant(start); + + // If the time hasn't past, then we don't apply it. + Instant now = timekeeper.Clock.GetCurrentInstant(); + + return instant > now + ? entity + : this.Apply(entity, instant); + } + + /// + public virtual bool CanApply(Entity entity) + { + return true; + } + + protected virtual Entity Apply( + Entity entity, + Instant instant) + { + return entity.Set(instant); + } +} diff --git a/src/MfGames.Nitride.Temporal.Schedules/Validators/ApplySchedulesValidator.cs b/src/MfGames.Nitride.Temporal.Schedules/Validators/ApplySchedulesValidator.cs index e525913..f1fe2de 100644 --- a/src/MfGames.Nitride.Temporal.Schedules/Validators/ApplySchedulesValidator.cs +++ b/src/MfGames.Nitride.Temporal.Schedules/Validators/ApplySchedulesValidator.cs @@ -6,8 +6,8 @@ public class ApplySchedulesValidator : AbstractValidator { public ApplySchedulesValidator() { - this.RuleFor(x => x.Schedules) - .NotEmpty(); + this.RuleFor(x => x.GetSchedules) + .NotNull(); this.RuleFor(x => x.Timekeeper) .NotNull(); diff --git a/tests/MfGames.Nitride.Temporal.Schedules.Tests/NumericalPathScheduleTests.cs b/tests/MfGames.Nitride.Temporal.Schedules.Tests/NumericalPathScheduleTests.cs index 72ca49c..76e6826 100644 --- a/tests/MfGames.Nitride.Temporal.Schedules.Tests/NumericalPathScheduleTests.cs +++ b/tests/MfGames.Nitride.Temporal.Schedules.Tests/NumericalPathScheduleTests.cs @@ -56,7 +56,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase // second but before the third item. Timekeeper time = context.Resolve(); ApplySchedules op = context.Resolve() - .WithSchedules(schedules); + .WithGetSchedules(_ => schedules); var now = Instant.FromUtc(2023, 1, 9, 0, 0); time.Clock = new FakeClock(now); @@ -111,7 +111,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase // second but before the third item. Timekeeper time = context.Resolve(); ApplySchedules op = context.Resolve() - .WithSchedules(schedules); + .WithGetSchedules(_ => schedules); var now = Instant.FromUtc(2023, 1, 9, 0, 0); time.Clock = new FakeClock(now); @@ -171,7 +171,7 @@ public class NumericalPathScheduleTests : TemporalSchedulesTestBase // second but before the third item. Timekeeper time = context.Resolve(); ApplySchedules op = context.Resolve() - .WithSchedules(schedules); + .WithGetSchedules(_ => schedules); var now = Instant.FromUtc(2023, 1, 9, 0, 0); time.Clock = new FakeClock(now);