feat: add Cake build to produce powershell module (#2)
- Add Cake build project - Change to CPM - Package bumps for security bumps - Reorganise solution structure to group harness projects
This commit is contained in:
parent
274d279732
commit
ecb0a20cbb
27 changed files with 267 additions and 58 deletions
173
src/PowershellModule/Calendar/GetCalendarCommand.cs
Normal file
173
src/PowershellModule/Calendar/GetCalendarCommand.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Management.Automation;
|
||||
|
||||
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 ModuleCore.Calendar.Calendar _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 ModuleCore.Calendar.Calendar.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,
|
||||
_ => ModuleCore.Calendar.Calendar.DefaultStartOfWeek
|
||||
};
|
||||
}
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var calendar = _calendar.RenderCalendar(_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;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
Loading…
Reference in a new issue