2025-08-14 15:37:53 -06:00

89 lines
2.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
#nullable enable
namespace LibationFileManager.Templates;
public class SeriesOrder : IFormattable
{
public object[] OrderParts { get; }
private SeriesOrder(object[] orderParts)
{
OrderParts = orderParts;
}
public override string ToString() => ToString(null, null);
/// <summary>
/// Use float formatters to format the number parts of the order.
/// </summary>
public string ToString(string? format, IFormatProvider? formatProvider)
=> string.Concat(OrderParts.Select(p => p is float f ? f.ToString(format) : p.ToString())).Trim();
public static SeriesOrder Parse(string? order)
{
List<object> parts = new();
while (TryParseNumber(order, out var value, out var range))
{
var prefix = order[..range.Start.Value];
if(!string.IsNullOrWhiteSpace(prefix))
parts.Add(prefix);
parts.Add(value);
order = order[range.End.Value..];
}
if (!string.IsNullOrWhiteSpace(order))
parts.Add(order);
return new(parts.ToArray());
}
/// <summary>
/// Try to parse any positive number from within the string (greedy).
/// </summary>
/// <param name="numString">the string to search for a numeric value</param>
/// <param name="value">If this function succeeds, the number that was found; otherwise zero.</param>
/// <param name="range">If this function succeeds, the range of characters representing <paramref name="value"/> in <paramref name="numString"/>; otherwise default</param>
/// <returns>True if a number was found; otherwise false.</returns>
private static bool TryParseNumber([NotNullWhen(true)] string? numString, out float value, out Range range)
{
value = 0;
if (string.IsNullOrWhiteSpace(numString))
{
range = default;
return false;
}
for (int s = 0; s < numString.Length; s++)
{
//Assume any valid number will begin with a digit.
//This way, leading dots and dashes will never be considered part of a number, so
//no negative series numbers and no fractional series numbers < 1 (unless preceded with a '0').
if (!char.IsDigit(numString[s]))
continue;
for (int e = numString.Length; e > s; e--)
{
//The float parser will succeed with trailing whitespace,
//but we want to preserve it in the final display string.
if (char.IsWhiteSpace(numString[e - 1]))
continue;
var substring = numString[s..e];
if (float.TryParse(substring, out value))
{
range = new Range(s, e);
return true;
}
}
}
range = default;
return false;
}
}