This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-nitride-cil/src/MfGames.Nitride.Temporal.Schedules/NumericalPathSchedule.cs
D. Moonfire 185980b5c4
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
fix(schedules): corrected serialization of pathRegex
2023-01-18 22:32:54 -06:00

142 lines
4.2 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 = @"^.*(\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 string? 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);
Regex? regex = this.GetRegex();
Match? match = regex?.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);
Regex? regex = this.GetRegex();
bool match = regex?.IsMatch(path) ?? false;
return match;
}
public Regex? GetRegex()
{
return this.PathRegex == null
? null
: new Regex(this.PathRegex);
}
protected virtual Entity Apply(
Entity entity,
int number,
Instant instant)
{
return entity.Set(instant);
}
}