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-toolbuilder-cil/src/MfGames.ToolBuilder/Extensions/StringCliExtensions.cs

138 lines
4.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using FluentResults;
namespace MfGames.ToolBuilder.Extensions
{
public static class StringCliExtensions
{
/// <summary>
/// Parses a string and converts it into an enumeration while ignoring
/// case and allow only prefix values. If the value cannot be
/// determined, it will report it and throw an exception.
/// </summary>
/// <typeparam name="TEnum">The enumeration to parse.</typeparam>
/// <param name="input">The input value to parse.</param>
/// <param name="logger">The logger to report issues.</param>
/// <param name="label">The name of the variable or label.</param>
/// <returns></returns>
public static TEnum GetEnumFuzzy<TEnum>(
this string input,
string label)
where TEnum : struct
{
if (input.TryParseEnumFuzzy(out TEnum value))
{
return value;
}
throw new InvalidOperationException(
string.Format(
"Cannot parse unknown value {0} for {1}: {2}",
input,
label,
Enum.GetNames(typeof(TEnum))));
}
/// <summary>
/// Searches for possible list of values for a given one, handling
/// case-insensitive searching and substring searches.
/// </summary>
/// <param name="value">The value to search for.</param>
/// <param name="possible">A collection of all possible values.</param>
/// <returns>A tuple with the selected value or an error message.</returns>
public static Result<string> GetFuzzy(
this string value,
ICollection<string> possible)
{
// Look for a direct match.
var found = possible
.Where(
x => string.Equals(
x,
value,
StringComparison.InvariantCultureIgnoreCase))
.ToList();
if (found.Count == 1)
{
return Result.Ok(found[0]);
}
// Look for substrings in the values.
found = possible
.Where(
x => x.Contains(
value,
StringComparison.InvariantCultureIgnoreCase))
.ToList();
return found.Count switch
{
1 => Result.Ok(found[0]),
0 => Result.Fail<string>(
string.Format(
"Cannot find \"{0}\" from possible options: {1}.",
value,
string.Join(", ", possible))),
_ => Result.Fail<string>(
string.Format(
"Found multiple matches for \"{0}\" from possible options: {1}.",
value,
string.Join(", ", found))),
};
}
/// <summary>
/// Parses a string and converts it into an enumeration while ignoring
/// case and allowing only prefix
/// values.
/// </summary>
/// <typeparam name="TEnum">The enumeration to parse.</typeparam>
/// <param name="input">The input value to parse.</param>
/// <param name="value">
/// The resulting value if parsed or default if not.
/// </param>
/// <returns>The resulting enum.</returns>
public static bool TryParseEnumFuzzy<TEnum>(
this string input,
out TEnum value)
where TEnum : struct
{
// If we have a blank, then we don't know what to do.
if (string.IsNullOrWhiteSpace(input))
{
value = default;
return false;
}
// See if we have an exact match first.
if (Enum.TryParse(input, true, out value))
{
return true;
}
// Attempt a fuzzy search.
var possible = Enum.GetNames(typeof(TEnum))
.Select(e => e.ToLower())
.Where(e => e.StartsWith(input.ToLower()))
.ToList();
if (possible.Count == 1)
{
value = (TEnum)Enum.Parse(typeof(TEnum), possible[0], true);
return true;
}
// Fall back to not allowing the value.
value = default;
return false;
}
}
}