174 lines
No EOL
5.9 KiB
C#
174 lines
No EOL
5.9 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.Management.Automation;
|
|
using ModuleCore.Calendar;
|
|
|
|
namespace PowershellModule.Calendar
|
|
{
|
|
[Cmdlet(VerbsCommon.Get, Noun)]
|
|
public class GetCalendarCommand : PSCmdlet
|
|
{
|
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
|
[Parameter(
|
|
Mandatory = false,
|
|
Position = 0)]
|
|
public string Date { get; set; }
|
|
|
|
[Parameter(
|
|
Mandatory = false,
|
|
Position = 1)]
|
|
public string MarkedDaySymbol { get; set; }
|
|
|
|
[Parameter(
|
|
Mandatory = false,
|
|
Position = 2)]
|
|
[ValidateSet("Sunday",
|
|
"Monday",
|
|
"Tuesday",
|
|
"Wednesday",
|
|
"Thursday",
|
|
"Friday",
|
|
"Saturday")]
|
|
// pass a type in to get dynamic types
|
|
// NOTE: the generator gets called for _each_ character input, so ensure the values are idempotent for every
|
|
// keydown (and fast), or cached
|
|
// [ValidateSet(typeof(StartDayOfWeekGenerator))]
|
|
public string StartOfWeek { get; set; }
|
|
|
|
[Parameter(
|
|
Mandatory = false,
|
|
Position = 3)]
|
|
public SwitchParameter AlignRight { get; set; }
|
|
|
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
|
|
|
// Guaranteed to be initialised in BeginProcessing. If it's not, something is cooked in some other way
|
|
private CalendarGenerator _calendar = null!;
|
|
private const string Noun = "Calendar";
|
|
public const string FullName = $"{VerbsCommon.Get}-{Noun}";
|
|
|
|
private DateTime _dateToRender = DateTime.Now;
|
|
private int _leftPadding;
|
|
|
|
protected override void BeginProcessing()
|
|
{
|
|
// This is the first point in script execution where we'll have parameters populated
|
|
_calendar = new(
|
|
MarkedDaySymbol,
|
|
DayOfWeekFromString(StartOfWeek)
|
|
);
|
|
|
|
if (!string.IsNullOrWhiteSpace(Date))
|
|
{
|
|
_dateToRender = DateTime.ParseExact(Date, CultureInfo.CurrentCulture.DateTimeFormat.GetAllDateTimePatterns(), null);
|
|
}
|
|
|
|
if (AlignRight)
|
|
{
|
|
_leftPadding = Host.UI.RawUI.WindowSize.Width;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a <see cref="DayOfWeek"/> from a string.
|
|
/// <para>
|
|
/// Defaults to the calendars default start of week on null or invalid values.
|
|
/// </para>
|
|
/// </summary>
|
|
/// <param name="dayOfWeek"></param>
|
|
/// <returns></returns>
|
|
private static DayOfWeek DayOfWeekFromString(string? dayOfWeek)
|
|
{
|
|
if (dayOfWeek is null)
|
|
{
|
|
return CalendarGenerator.DefaultStartOfWeek;
|
|
}
|
|
|
|
return dayOfWeek.ToLowerInvariant() switch
|
|
{
|
|
"sunday" => DayOfWeek.Sunday,
|
|
"monday" => DayOfWeek.Monday,
|
|
"tuesday" => DayOfWeek.Tuesday,
|
|
"wednesday" => DayOfWeek.Wednesday,
|
|
"thursday" => DayOfWeek.Thursday,
|
|
"friday" => DayOfWeek.Friday,
|
|
"saturday" => DayOfWeek.Saturday,
|
|
_ => CalendarGenerator.DefaultStartOfWeek
|
|
};
|
|
}
|
|
|
|
protected override void ProcessRecord()
|
|
{
|
|
var calendar = _calendar.Render(_dateToRender);
|
|
|
|
// From _very_ basic testing, string split allocates the same as making a span and iterating over it.
|
|
// I'm assuming this is because a lot of the span code is what string.Split() does internally and the rest
|
|
// is trivially optimised out.
|
|
// Not too fussed currently but it's most likely because Pad methods also allocate a string, so they end up
|
|
// equivalent at the end
|
|
// OutputCalendarStringSplit(calendar);
|
|
OutputCalendar(calendar);
|
|
|
|
base.ProcessRecord();
|
|
}
|
|
|
|
private void OutputCalendarStringSplit(string calendar)
|
|
{
|
|
var splitCalendarLines = calendar.Split(Environment.NewLine);
|
|
foreach (var splitLines in splitCalendarLines)
|
|
{
|
|
// Have to output a newline character here
|
|
WriteObject(splitLines.PadLeft(_leftPadding));
|
|
}
|
|
}
|
|
|
|
private void OutputCalendar(string calendar)
|
|
{
|
|
// Work on a span - we avoid using string.Split() as this allocates on certain runtimes
|
|
var calendarSpan = calendar.AsSpan();
|
|
// Get the end of line for the calendar. We use Environment.NewLine as the calendar is built up using
|
|
// StringBuilder which uses Environment.NewLine for new lines so this should be consistent across operating systems
|
|
var firstNewlineIndex = calendarSpan.IndexOf(Environment.NewLine);
|
|
// Calculate how many lines the calendar is from how many new lines we have.
|
|
// Sure we could count the number of \r\n are present, but the last line might not end with
|
|
// a new line.
|
|
// Conveniently enough, int division works out in our favor here
|
|
var lines = calendarSpan.Length / firstNewlineIndex;
|
|
|
|
// We need to account for the size of the newline so we have an accurate length when outputting each line
|
|
// of the calendar below
|
|
var newLineLength = firstNewlineIndex + Environment.NewLine.Length;
|
|
|
|
for (int i = 0; i < lines; i++)
|
|
{
|
|
// The start of each calendar line offset starts at a position including the length of the newline,
|
|
// so in effect the start of the line is the length of the previous line output, plus however many
|
|
// characters the newline was that weren't output as we're taking control there
|
|
var startOfLine = i * newLineLength;
|
|
// End of the line is exclusive of any newline characters
|
|
var endOfLine = startOfLine + firstNewlineIndex;
|
|
var calendarLine = calendarSpan[startOfLine .. endOfLine];
|
|
|
|
WriteObject(calendarLine.ToString().PadLeft(_leftPadding));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Was here to test dynamic day names but most people using the console will be using english command names
|
|
// so there's at least some assumption that they understand days of the week in english.
|
|
// It sucks but it remains at least consistent with the argument being in english as well.
|
|
// public class StartDayOfWeekGenerator : IValidateSetValuesGenerator
|
|
// {
|
|
// private string[] _cached { get; set; } = new string[7];
|
|
//
|
|
// public string[] GetValidValues()
|
|
// {
|
|
// if (_cached is null)
|
|
// {
|
|
// // _cached = Enumerable.Range(0, r.Next(3, 63)).Select(x => x.ToString()).ToArray();
|
|
// }
|
|
//
|
|
// return _cached;
|
|
// }
|
|
// }
|
|
} |