138 lines
4.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|