using System;
using System.Collections.Generic;
using System.Linq;
using FluentResults;
namespace MfGames.ToolBuilder.Extensions
{
public static class StringCliExtensions
{
///
/// 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.
///
/// The enumeration to parse.
/// The input value to parse.
/// The logger to report issues.
/// The name of the variable or label.
///
public static TEnum GetEnumFuzzy(
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))));
}
///
/// Searches for possible list of values for a given one, handling
/// case-insensitive searching and substring searches.
///
/// The value to search for.
/// A collection of all possible values.
/// A tuple with the selected value or an error message.
public static Result GetFuzzy(
this string value,
ICollection 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.Format(
"Cannot find \"{0}\" from possible options: {1}.",
value,
string.Join(", ", possible))),
_ => Result.Fail(
string.Format(
"Found multiple matches for \"{0}\" from possible options: {1}.",
value,
string.Join(", ", found))),
};
}
///
/// Parses a string and converts it into an enumeration while ignoring
/// case and allowing only prefix
/// values.
///
/// The enumeration to parse.
/// The input value to parse.
///
/// The resulting value if parsed or default if not.
///
/// The resulting enum.
public static bool TryParseEnumFuzzy(
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;
}
}
}