using System; using System.Collections.Generic; using System.Linq; using System.Threading; using FluentValidation; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; using Ical.Net.Serialization; using MfGames.Gallium; using MfGames.Nitride.Contents; using MfGames.Nitride.Generators; using MfGames.Nitride.Temporal; using NodaTime; using Zio; namespace MfGames.Nitride.Calendar; /// /// Creates an iCalendar file from all the entities passed into the method /// that have a NodaTime.Instant component. This will write both past and /// future events. /// [WithProperties] public partial class CreateCalender : OperationBase { private readonly TimeService clock; private readonly IValidator validator; public CreateCalender( IValidator validator, TimeService clock) { this.validator = validator; this.clock = clock; } /// /// Gets or sets a callback to get the summary of the event representing /// the entity. /// public Func? GetEventSummary { get; set; } /// /// Gets or sets a callback to get the optional URL of an event for /// the entity. /// public Func? GetEventUrl { get; set; } /// /// Gets or sets the file system path for the resulting calendar. /// public UPath? Path { get; set; } /// public override IEnumerable Run( IEnumerable input, CancellationToken cancellationToken = default) { this.validator.ValidateAndThrow(this); SplitEntityEnumerations split = input.SplitEntity(); IEnumerable datedAndCalendars = this.CreateCalendarEntity(split.HasAll); return datedAndCalendars.Union(split.NotHasAll); } private IEnumerable CreateCalendarEntity( IEnumerable entities) { // Create the calendar in the same time zone as the rest of the system. var calendar = new Ical.Net.Calendar(); calendar.TimeZones.Add(new VTimeZone(this.clock.DateTimeZone.Id)); // Go through the events and add all of them. var input = entities.ToList(); IEnumerable events = input.Select(this.CreateCalendarEvent); calendar.Events.AddRange(events); // Create the iCalendar file. var serializer = new CalendarSerializer(); string serializedCalendar = serializer.SerializeToString(calendar); // Create the calendar entity and populate everything. Entity calendarEntity = new Entity().Set(IsCalendar.Instance) .Set(this.Path!.Value) .SetTextContent(serializedCalendar); // Return the results along with the new calendar. return input.Union(new[] { calendarEntity }); } private CalendarEvent CreateCalendarEvent(Entity entity) { Instant instant = entity.Get(); var when = this.clock.ToDateTime(instant); string summary = this.GetEventSummary!(entity); Uri? url = this.GetEventUrl?.Invoke(entity); var calendarEvent = new CalendarEvent { Summary = summary, Start = new CalDateTime(when), Url = url, }; return calendarEvent; } }