133 lines
4 KiB
C#
133 lines
4 KiB
C#
using System;
|
|
using System.Text.RegularExpressions;
|
|
|
|
using MfGames.Gallium;
|
|
using MfGames.Nitride.Generators;
|
|
|
|
using NodaTime;
|
|
|
|
using TimeSpanParserUtil;
|
|
|
|
using Zio;
|
|
|
|
namespace MfGames.Nitride.Temporal.Schedules;
|
|
|
|
/// <summary>
|
|
/// A schedule that uses the `UPath` of the entity to determine an offset from
|
|
/// a starting point and calculates the information from there.
|
|
/// </summary>
|
|
[WithProperties]
|
|
public partial class NumericalPathSchedule : ISchedule
|
|
{
|
|
public NumericalPathSchedule()
|
|
{
|
|
this.PathRegex = new Regex(@"^.*(\d+)");
|
|
this.GetPath = entity => entity.Get<UPath>().ToString();
|
|
this.CaptureGroup = 1;
|
|
this.CaptureOffset = -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the group number of the capture group with 1 being being the
|
|
/// first group in PathRegex.
|
|
/// </summary>
|
|
public int CaptureGroup { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the offset to make the resulting capture group a zero-based
|
|
/// number.
|
|
/// </summary>
|
|
public int CaptureOffset { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the method for retrieving the path for the entity.
|
|
/// </summary>
|
|
public Func<Entity, string> GetPath { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the regular expression to identify a matching path.
|
|
/// </summary>
|
|
public Regex? PathRegex { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the period between each matching entity. The period is
|
|
/// amount of time between each item based on the number. So the first will
|
|
/// be on the ScheduleDate, the second SchedulePeriod later, the third
|
|
/// SchedulePeriod after the second, etc. More precisely, the date for
|
|
/// any item TimeSpan * ((int)CaptureGroup + (int)CaptureOffset).
|
|
/// </summary>
|
|
public string? SchedulePeriod { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the schedule period as a TimeSpan object. This is converted
|
|
/// from SchedulePeriod using https://github.com/pengowray/TimeSpanParser.
|
|
/// If the value is null, then this will be instant (TimeSpan.Zero).
|
|
/// </summary>
|
|
public virtual TimeSpan SchedulePeriodTimeSpan
|
|
{
|
|
get => this.SchedulePeriod == null
|
|
? TimeSpan.Zero
|
|
: TimeSpanParser.Parse(this.SchedulePeriod);
|
|
set => this.SchedulePeriod = value.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets when the first item is scheduled.
|
|
/// </summary>
|
|
public DateTime? ScheduleStart { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public virtual Entity Apply(
|
|
Entity entity,
|
|
Timekeeper timekeeper)
|
|
{
|
|
// Get the path and match it.
|
|
string? path = this.GetPath(entity);
|
|
Match? match = this.PathRegex?.Match(path)
|
|
?? throw new NullReferenceException(
|
|
"PathRegex was not configured for the "
|
|
+ this.GetType().Name
|
|
+ ".");
|
|
|
|
if (!match.Success)
|
|
{
|
|
return entity;
|
|
}
|
|
|
|
// Figure out the index/number of this entry.
|
|
int number = int.Parse(match.Groups[this.CaptureGroup].Value)
|
|
+ this.CaptureOffset;
|
|
|
|
// Figure out the time from the start.
|
|
DateTime start = this.ScheduleStart
|
|
?? throw new NullReferenceException(
|
|
"Cannot use a schedule without a start date.");
|
|
DateTime when = start + this.SchedulePeriodTimeSpan * number;
|
|
Instant instant = timekeeper.CreateInstant(when);
|
|
|
|
// If the time hasn't past, then we don't apply it.
|
|
Instant now = timekeeper.Clock.GetCurrentInstant();
|
|
|
|
return instant > now
|
|
? entity
|
|
: this.Apply(entity, number, instant);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual bool CanApply(Entity entity)
|
|
{
|
|
string? path = this.GetPath(entity);
|
|
bool match = this.PathRegex?.IsMatch(path) ?? false;
|
|
|
|
return match;
|
|
}
|
|
|
|
protected virtual Entity Apply(
|
|
Entity entity,
|
|
int number,
|
|
Instant instant)
|
|
{
|
|
return entity.Set(instant);
|
|
}
|
|
}
|